Why avoid increment ("++") and decrement ("--") operators in JavaScript? - javascript
One of the tips for jslint tool is:
++ and --
The ++ (increment) and -- (decrement)
operators have been known to contribute to bad code by
encouraging excessive trickiness. They
are second only to faulty architecture
in enabling to viruses and other
security menaces. There is a plusplus
option that prohibits the use of these
operators.
I know that PHP constructs like $foo[$bar++] may easily result in off-by-one errors, but I couldn't figure out a better way to control the loop than a:
while( a < 10 ) do { /* foo */ a++; }
or
for (var i=0; i<10; i++) { /* foo */ }
Is the jslint highlighting them because there are some similar languages that lack the "++" and "--" syntax or handle it differently, or are there other rationales for avoiding "++" and "--" that I might be missing?
My view is to always use ++ and -- by themselves on a single line, as in:
i++;
array[i] = foo;
instead of
array[++i] = foo;
Anything beyond that can be confusing to some programmers and is just not worth it in my view. For loops are an exception, as the use of the increment operator is idiomatic and thus always clear.
There is a history in C of doing things like:
while (*a++ = *b++);
to copy a string, perhaps this is the source of the excessive trickery he is referring to.
And there's always the question of what
++i = i++;
or
i = i++ + ++i;
actually do. It's defined in some languages, and in other's there's no guarantee what will happen.
Those examples aside, I don't think there's anything more idiomatic than a for loop that uses ++ to increment. In some cases you could get away with a foreach loop, or a while loop that checked a different condtion. But contorting your code to try and avoid using incrementing is ridiculous.
If you read JavaScript The Good Parts, you'll see that Crockford's replacement for i++ in a for loop is i+=1 (not i=i+1). That's pretty clean and readable, and is less likely to morph into something "tricky."
Crockford made disallowing autoincrement and autodecrement an option in jsLint. You choose whether to follow the advice or not.
My own personal rule is to not do anything combined with autoincrement or autodecrement.
I've learned from years of experience in C that I don't get buffer overruns (or array index out of bounds) if I keep use of it simple. But I've discovered that I do get buffer overruns if I fall into the "excessively tricky" practice of doing other things in the same statement.
So, for my own rules, the use of i++ as the increment in a for loop is fine.
In a loop it's harmless, but in an assignment statement it can lead to unexpected results:
var x = 5;
var y = x++; // y is now 5 and x is 6
var z = ++x; // z is now 7 and x is 7
Whitespace between the variable and the operator can lead to unexpected results as well:
a = b = c = 1; a ++ ; b -- ; c; console.log('a:', a, 'b:', b, 'c:', c)
In a closure, unexpected results can be an issue as well:
var foobar = function(i){var count = count || i; return function(){return count++;}}
baz = foobar(1);
baz(); //1
baz(); //2
var alphabeta = function(i){var count = count || i; return function(){return ++count;}}
omega = alphabeta(1);
omega(); //2
omega(); //3
And it triggers automatic semicolon insertion after a newline:
var foo = 1, bar = 2, baz = 3, alpha = 4, beta = 5, delta = alpha
++beta; //delta is 4, alpha is 4, beta is 6
preincrement/postincrement confusion can produce off-by-one errors that are extremely difficult to diagnose. Fortunately, they are also complete unnecessary. There are better ways to add 1 to a variable.
References
JSLint Help: Increment and Decrement Operators
The "pre" and "post" nature of increment and decrement operators can tend to be confusing for those who are not familiar with them; that's one way in which they can be tricky.
Consider the following code
int a[10];
a[0] = 0;
a[1] = 0;
a[2] = 0;
a[3] = 0;
int i = 0;
a[i++] = i++;
a[i++] = i++;
a[i++] = i++;
since i++ gets evaluated twice the output is
(from vs2005 debugger)
[0] 0 int
[1] 0 int
[2] 2 int
[3] 0 int
[4] 4 int
Now consider the following code :
int a[10];
a[0] = 0;
a[1] = 0;
a[2] = 0;
a[3] = 0;
int i = 0;
a[++i] = ++i;
a[++i] = ++i;
a[++i] = ++i;
Notice that the output is the same. Now you might think that ++i and i++ are the same. They are not
[0] 0 int
[1] 0 int
[2] 2 int
[3] 0 int
[4] 4 int
Finally consider this code
int a[10];
a[0] = 0;
a[1] = 0;
a[2] = 0;
a[3] = 0;
int i = 0;
a[++i] = i++;
a[++i] = i++;
a[++i] = i++;
The output is now :
[0] 0 int
[1] 1 int
[2] 0 int
[3] 3 int
[4] 0 int
[5] 5 int
So they are not the same, mixing both result in not so intuitive behavior. I think that for loops are ok with ++, but watch out when you have multiple ++ symbols on the same line or same instruction
In my view, "Explicit is always better than implicit." Because at some point, you may got confused with this increments statement y+ = x++ + ++y. A good programmer always makes his or her code more readable.
I've been watching Douglas Crockford's video on this and his explanation for not using increment and decrement is that
It has been used in the past in other languages to break the bounds of arrays and cause all manners of badness and
That it is more confusing and inexperienced JS developers don't know exactly what it does.
Firstly arrays in JavaScript are dynamically sized and so, forgive me if I'm wrong, it is not possible to break the bounds of an array and access data that shouldn't be accessed using this method in JavaScript.
Secondly, should we avoid things that are complicated, surely the problem is not that we have this facility but the problem is that there are developers out there that claim to do JavaScript but don't know how these operators work?? It is simple enough. value++, give me the current value and after the expression add one to it, ++value, increment the value before giving me it.
Expressions like a ++ + ++ b, are simple to work out if you just remember the above.
var a = 1, b = 1, c;
c = a ++ + ++ b;
// c = 1 + 2 = 3;
// a = 2 (equals two after the expression is finished);
// b = 2;
I suppose you've just got to remember who has to read through the code, if you have a team that knows JS inside out then you don't need to worry. If not then comment it, write it differently, etc. Do what you got to do. I don't think increment and decrement is inherently bad or bug generating, or vulnerability creating, maybe just less readable depending on your audience.
Btw, I think Douglas Crockford is a legend anyway, but I think he's caused a lot of scare over an operator that didn't deserve it.
I live to be proven wrong though...
The most important rationale for avoiding ++ or -- is that the operators return values and cause side effects at the same time, making it harder to reason about the code.
For efficiency's sake, I prefer:
++i when not using the return value (no temporary)
i++ when using the return value (no pipeline stall)
I am a fan of Mr. Crockford, but in this case I have to disagree. ++i is 25% less text to parse than i+=1 and arguably clearer.
Another example, more simple than some others with simple return of incremented value:
function testIncrement1(x) {
return x++;
}
function testIncrement2(x) {
return ++x;
}
function testIncrement3(x) {
return x += 1;
}
console.log(testIncrement1(0)); // 0
console.log(testIncrement2(0)); // 1
console.log(testIncrement3(0)); // 1
As you can see, no post-increment/decrement should be used at return statement, if you want this operator to influence the result. But return doesn't "catch" post-increment/decrement operators:
function closureIncrementTest() {
var x = 0;
function postIncrementX() {
return x++;
}
var y = postIncrementX();
console.log(x); // 1
}
I think programmers should be competent in the language they are using; use it clearly; and use it well. I don't think they should artificially cripple the language they are using. I speak from experience. I once worked literally next door to a Cobol shop where they didn't use ELSE 'because it was too complicated'. Reductio ad absurdam.
In my experience, ++i or i++ has never caused confusion other than when first learning about how the operator works. It is essential for the most basic for loops and while loops that are taught by any highschool or college course taught in languages where you can use the operator. I personally find doing something like what is below to look and read better than something with a++ being on a separate line.
while ( a < 10 ){
array[a++] = val
}
In the end it is a style preference and not anything more, what is more important is that when you do this in your code you stay consistent so that others working on the same code can follow and not have to process the same functionality in different ways.
Also, Crockford seems to use i-=1, which I find to be harder to read than --i or i--
As mentioned in some of the existing answers (which annoyingly I'm unable to comment on), the problem is that x++ ++x evaluate to different values (before vs after the increment), which is not obvious and can be very confusing - if that value is used. cdmckay suggests quite wisely to allow use of increment operator, but only in a way that the returned value is not used, e.g. on its own line. I would also include the standard use within a for loop (but only in the third statement, whose return value is not used). I can't think of another example. Having been "burnt" myself, I would recommend the same guideline for other languages as well.
I disagree with the claim that this over-strictness is due to a lot of JS programmers being inexperienced. This is the exact kind of writing typical of "overly-clever" programmers, and I'm sure it's much more common in more traditional languages and with JS developers who have a background in such languages.
My 2cents is that they should be avoided in two cases:
1) When you have a variable that is used in many rows and you increase/decrease it on the first statement that uses it (or last, or, even worse, in the middle):
// It's Java, but applies to Js too
vi = list.get ( ++i );
vi1 = list.get ( i + 1 )
out.println ( "Processing values: " + vi + ", " + vi1 )
if ( i < list.size () - 1 ) ...
In examples like this, you can easily miss that the variable is auto-incremented/decremented or even remove the first statement. In other words, use it only in very short blocks or where the variable appears in the block on just a couple of close statements.
2) In case of multiple ++ and -- about the same variable in the same statement. It's very hard to remember what happens in cases like this:
result = ( ++x - --x ) * x++;
Exams and professional tests asks about examples like above and indeed I've stumbled upon this question while looking for documentation about one of them, but in real life one shouldn't be forced to think so much about a single line of code.
Is Fortran a C-like language? It has neither ++ nor --. Here is how you write a loop:
integer i, n, sum
sum = 0
do 10 i = 1, n
sum = sum + i
write(*,*) 'i =', i
write(*,*) 'sum =', sum
10 continue
The index element i is incremented by the language rules each time through the loop. If you want to increment by something other than 1, count backwards by two for instance, the syntax is ...
integer i
do 20 i = 10, 1, -2
write(*,*) 'i =', i
20 continue
Is Python C-like? It uses range and list comprehensions and other syntaxes to bypass the need for incrementing an index:
print range(10,1,-2) # prints [10,8.6.4.2]
[x*x for x in range(1,10)] # returns [1,4,9,16 ... ]
So based on this rudimentary exploration of exactly two alternatives, language designers may avoid ++ and -- by anticipating use cases and providing an alternate syntax.
Are Fortran and Python notably less of a bug magnet than procedural languages which have ++ and --? I have no evidence.
I claim that Fortran and Python are C-like because I have never met someone fluent in C who could not with 90% accuracy guess correctly the intent of non-obfuscated Fortran or Python.
The operators mean different things when used as prefixes versus suffixes, which can cause hard-to-find bugs. Consider the following example, using bubbleSort:
function bubbleSort(array) {
if(array.length === 1) return array;
let end = array.length - 2;
do {
for (let i = 0; i < array.length; i += 1) {
if (array[i] > array[i + 1]) {
swap(array, i, i + 1);
}
}
} while (end--);
}
bubbleSort([6,5]);
Let's imagine in the course of running our program, we pass a two-item value into our sort function. The code runs fine as-is: the "do/while" loop first executes before reaching the condition. However, the program sees that end is falsy and exits the loop before decrementing the variable.
Now consider the following code, where the -- symbol is used as a prefix, rather than a suffix. This code will enter an infinite loop:
function bubbleSort(array) {
if(array.length === 1) return array;
let end = array.length - 2;
do {
for (let i = 0; i < array.length; i += 1) {
if (array[i] > array[i + 1]) {
swap(array, i, i + 1);
}
}
} while (--end);
}
bubbleSort([6,5]);
Now when we hit the while condition, we decrement the end value before checking it. This returns -1, which in Javascript, is a truthy value.
I don't have a strong opinion on their use one way or the other, but I just wanted to show how they can cause real bugs when used carelessly.
Argument for ++ is that ++ is not applicable for strings which will cause TypeError, which is almost always preferable.
Related
Multiply a string by itself in javascript [duplicate]
This question already has answers here: Repeat a string in JavaScript a number of times (24 answers) Closed last year. What is the best or most concise method for returning a string repeated an arbitrary amount of times? The following is my best shot so far: function repeat(s, n){ var a = []; while(a.length < n){ a.push(s); } return a.join(''); }
Note to new readers: This answer is old and and not terribly practical - it's just "clever" because it uses Array stuff to get String things done. When I wrote "less process" I definitely meant "less code" because, as others have noted in subsequent answers, it performs like a pig. So don't use it if speed matters to you. I'd put this function onto the String object directly. Instead of creating an array, filling it, and joining it with an empty char, just create an array of the proper length, and join it with your desired string. Same result, less process! String.prototype.repeat = function( num ) { return new Array( num + 1 ).join( this ); } alert( "string to repeat\n".repeat( 4 ) );
I've tested the performance of all the proposed approaches. Here is the fastest variant I've got. String.prototype.repeat = function(count) { if (count < 1) return ''; var result = '', pattern = this.valueOf(); while (count > 1) { if (count & 1) result += pattern; count >>= 1, pattern += pattern; } return result + pattern; }; Or as stand-alone function: function repeat(pattern, count) { if (count < 1) return ''; var result = ''; while (count > 1) { if (count & 1) result += pattern; count >>= 1, pattern += pattern; } return result + pattern; } It is based on wnrph's algorithm. It is really fast. And the bigger the count, the faster it goes compared with the traditional new Array(count + 1).join(string) approach. I've only changed 2 things: replaced pattern = this with pattern = this.valueOf() (clears one obvious type conversion); added if (count < 1) check from prototypejs to the top of function to exclude unnecessary actions in that case. applied optimisation from Dennis answer (5-7% speed up) UPD Created a little performance-testing playground here for those who interested. variable count ~ 0 .. 100: constant count = 1024: Use it and make it even faster if you can :)
This problem is a well-known / "classic" optimization issue for JavaScript, caused by the fact that JavaScript strings are "immutable" and addition by concatenation of even a single character to a string requires creation of, including memory allocation for and copying to, an entire new string. Unfortunately, the accepted answer on this page is wrong, where "wrong" means by a performance factor of 3x for simple one-character strings, and 8x-97x for short strings repeated more times, to 300x for repeating sentences, and infinitely wrong when taking the limit of the ratios of complexity of the algorithms as n goes to infinity. Also, there is another answer on this page which is almost right (based on one of the many generations and variations of the correct solution circulating throughout the Internet in the past 13 years). However, this "almost right" solution misses a key point of the correct algorithm causing a 50% performance degradation. JS Performance Results for the accepted answer, the top-performing other answer (based on a degraded version of the original algorithm in this answer), and this answer using my algorithm created 13 years ago ~ October 2000 I published an algorithm for this exact problem which was widely adapted, modified, then eventually poorly understood and forgotten. To remedy this issue, in August, 2008 I published an article http://www.webreference.com/programming/javascript/jkm3/3.html explaining the algorithm and using it as an example of simple of general-purpose JavaScript optimizations. By now, Web Reference has scrubbed my contact information and even my name from this article. And once again, the algorithm has been widely adapted, modified, then poorly understood and largely forgotten. Original string repetition/multiplication JavaScript algorithm by Joseph Myers, circa Y2K as a text multiplying function within Text.js; published August, 2008 in this form by Web Reference: http://www.webreference.com/programming/javascript/jkm3/3.html (The article used the function as an example of JavaScript optimizations, which is the only for the strange name "stringFill3.") /* * Usage: stringFill3("abc", 2) == "abcabc" */ function stringFill3(x, n) { var s = ''; for (;;) { if (n & 1) s += x; n >>= 1; if (n) x += x; else break; } return s; } Within two months after publication of that article, this same question was posted to Stack Overflow and flew under my radar until now, when apparently the original algorithm for this problem has once again been forgotten. The best solution available on this Stack Overflow page is a modified version of my solution, possibly separated by several generations. Unfortunately, the modifications ruined the solution's optimality. In fact, by changing the structure of the loop from my original, the modified solution performs a completely unneeded extra step of exponential duplicating (thus joining the largest string used in the proper answer with itself an extra time and then discarding it). Below ensues a discussion of some JavaScript optimizations related to all of the answers to this problem and for the benefit of all. Technique: Avoid references to objects or object properties To illustrate how this technique works, we use a real-life JavaScript function which creates strings of whatever length is needed. And as we'll see, more optimizations can be added! A function like the one used here is to create padding to align columns of text, for formatting money, or for filling block data up to the boundary. A text generation function also allows variable length input for testing any other function that operates on text. This function is one of the important components of the JavaScript text processing module. As we proceed, we will be covering two more of the most important optimization techniques while developing the original code into an optimized algorithm for creating strings. The final result is an industrial-strength, high-performance function that I've used everywhere--aligning item prices and totals in JavaScript order forms, data formatting and email / text message formatting and many other uses. Original code for creating strings stringFill1() function stringFill1(x, n) { var s = ''; while (s.length < n) s += x; return s; } /* Example of output: stringFill1('x', 3) == 'xxx' */ The syntax is here is clear. As you can see, we've used local function variables already, before going on to more optimizations. Be aware that there's one innocent reference to an object property s.length in the code that hurts its performance. Even worse, the use of this object property reduces the simplicity of the program by making the assumption that the reader knows about the properties of JavaScript string objects. The use of this object property destroys the generality of the computer program. The program assumes that x must be a string of length one. This limits the application of the stringFill1() function to anything except repetition of single characters. Even single characters cannot be used if they contain multiple bytes like the HTML entity . The worst problem caused by this unnecessary use of an object property is that the function creates an infinite loop if tested on an empty input string x. To check generality, apply a program to the smallest possible amount of input. A program which crashes when asked to exceed the amount of available memory has an excuse. A program like this one which crashes when asked to produce nothing is unacceptable. Sometimes pretty code is poisonous code. Simplicity may be an ambiguous goal of computer programming, but generally it's not. When a program lacks any reasonable level of generality, it's not valid to say, "The program is good enough as far as it goes." As you can see, using the string.length property prevents this program from working in a general setting, and in fact, the incorrect program is ready to cause a browser or system crash. Is there a way to improve the performance of this JavaScript as well as take care of these two serious problems? Of course. Just use integers. Optimized code for creating strings stringFill2() function stringFill2(x, n) { var s = ''; while (n-- > 0) s += x; return s; } Timing code to compare stringFill1() and stringFill2() function testFill(functionToBeTested, outputSize) { var i = 0, t0 = new Date(); do { functionToBeTested('x', outputSize); t = new Date() - t0; i++; } while (t < 2000); return t/i/1000; } seconds1 = testFill(stringFill1, 100); seconds2 = testFill(stringFill2, 100); The success so far of stringFill2() stringFill1() takes 47.297 microseconds (millionths of a second) to fill a 100-byte string, and stringFill2() takes 27.68 microseconds to do the same thing. That's almost a doubling in performance by avoiding a reference to an object property. Technique: Avoid adding short strings to long strings Our previous result looked good--very good, in fact. The improved function stringFill2() is much faster due to the use of our first two optimizations. Would you believe it if I told you that it can be improved to be many times faster than it is now? Yes, we can accomplish that goal. Right now we need to explain how we avoid appending short strings to long strings. The short-term behavior appears to be quite good, in comparison to our original function. Computer scientists like to analyze the "asymptotic behavior" of a function or computer program algorithm, which means to study its long-term behavior by testing it with larger inputs. Sometimes without doing further tests, one never becomes aware of ways that a computer program could be improved. To see what will happen, we're going to create a 200-byte string. The problem that shows up with stringFill2() Using our timing function, we find that the time increases to 62.54 microseconds for a 200-byte string, compared to 27.68 for a 100-byte string. It seems like the time should be doubled for doing twice as much work, but instead it's tripled or quadrupled. From programming experience, this result seems strange, because if anything, the function should be slightly faster since work is being done more efficiently (200 bytes per function call rather than 100 bytes per function call). This issue has to do with an insidious property of JavaScript strings: JavaScript strings are "immutable." Immutable means that you cannot change a string once it's created. By adding on one byte at a time, we're not using up one more byte of effort. We're actually recreating the entire string plus one more byte. In effect, to add one more byte to a 100-byte string, it takes 101 bytes worth of work. Let's briefly analyze the computational cost for creating a string of N bytes. The cost of adding the first byte is 1 unit of computational effort. The cost of adding the second byte isn't one unit but 2 units (copying the first byte to a new string object as well as adding the second byte). The third byte requires a cost of 3 units, etc. C(N) = 1 + 2 + 3 + ... + N = N(N+1)/2 = O(N^2). The symbol O(N^2) is pronounced Big O of N squared, and it means that the computational cost in the long run is proportional to the square of the string length. To create 100 characters takes 10,000 units of work, and to create 200 characters takes 40,000 units of work. This is why it took more than twice as long to create 200 characters than 100 characters. In fact, it should have taken four times as long. Our programming experience was correct in that the work is being done slightly more efficiently for longer strings, and hence it took only about three times as long. Once the overhead of the function call becomes negligible as to how long of a string we're creating, it will actually take four times as much time to create a string twice as long. (Historical note: This analysis doesn't necessarily apply to strings in source code, such as html = 'abcd\n' + 'efgh\n' + ... + 'xyz.\n', since the JavaScript source code compiler can join the strings together before making them into a JavaScript string object. Just a few years ago, the KJS implementation of JavaScript would freeze or crash when loading long strings of source code joined by plus signs. Since the computational time was O(N^2) it wasn't difficult to make Web pages which overloaded the Konqueror Web browser or Safari, which used the KJS JavaScript engine core. I first came across this issue when I was developing a markup language and JavaScript markup language parser, and then I discovered what was causing the problem when I wrote my script for JavaScript Includes.) Clearly this rapid degradation of performance is a huge problem. How can we deal with it, given that we cannot change JavaScript's way of handling strings as immutable objects? The solution is to use an algorithm which recreates the string as few times as possible. To clarify, our goal is to avoid adding short strings to long strings, since in order to add the short string, the entire long string also must be duplicated. How the algorithm works to avoid adding short strings to long strings Here's a good way to reduce the number of times new string objects are created. Concatenate longer lengths of string together so that more than one byte at a time is added to the output. For instance, to make a string of length N = 9: x = 'x'; s = ''; s += x; /* Now s = 'x' */ x += x; /* Now x = 'xx' */ x += x; /* Now x = 'xxxx' */ x += x; /* Now x = 'xxxxxxxx' */ s += x; /* Now s = 'xxxxxxxxx' as desired */ Doing this required creating a string of length 1, creating a string of length 2, creating a string of length 4, creating a string of length 8, and finally, creating a string of length 9. How much cost have we saved? Old cost C(9) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 9 = 45. New cost C(9) = 1 + 2 + 4 + 8 + 9 = 24. Note that we had to add a string of length 1 to a string of length 0, then a string of length 1 to a string of length 1, then a string of length 2 to a string of length 2, then a string of length 4 to a string of length 4, then a string of length 8 to a string of length 1, in order to obtain a string of length 9. What we're doing can be summarized as avoiding adding short strings to long strings, or in other words, trying to concatenate strings together that are of equal or nearly equal length. For the old computational cost we found a formula N(N+1)/2. Is there a formula for the new cost? Yes, but it's complicated. The important thing is that it is O(N), and so doubling the string length will approximately double the amount of work rather than quadrupling it. The code that implements this new idea is nearly as complicated as the formula for the computational cost. When you read it, remember that >>= 1 means to shift right by 1 byte. So if n = 10011 is a binary number, then n >>= 1 results in the value n = 1001. The other part of the code you might not recognize is the bitwise and operator, written &. The expression n & 1 evaluates true if the last binary digit of n is 1, and false if the last binary digit of n is 0. New highly-efficient stringFill3() function function stringFill3(x, n) { var s = ''; for (;;) { if (n & 1) s += x; n >>= 1; if (n) x += x; else break; } return s; } It looks ugly to the untrained eye, but it's performance is nothing less than lovely. Let's see just how well this function performs. After seeing the results, it's likely that you'll never forget the difference between an O(N^2) algorithm and an O(N) algorithm. stringFill1() takes 88.7 microseconds (millionths of a second) to create a 200-byte string, stringFill2() takes 62.54, and stringFill3() takes only 4.608. What made this algorithm so much better? All of the functions took advantage of using local function variables, but taking advantage of the second and third optimization techniques added a twenty-fold improvement to performance of stringFill3(). Deeper analysis What makes this particular function blow the competition out of the water? As I've mentioned, the reason that both of these functions, stringFill1() and stringFill2(), run so slowly is that JavaScript strings are immutable. Memory cannot be reallocated to allow one more byte at a time to be appended to the string data stored by JavaScript. Every time one more byte is added to the end of the string, the entire string is regenerated from beginning to end. Thus, in order to improve the script's performance, one must precompute longer length strings by concatenating two strings together ahead of time, and then recursively building up the desired string length. For instance, to create a 16-letter byte string, first a two byte string would be precomputed. Then the two byte string would be reused to precompute a four-byte string. Then the four-byte string would be reused to precompute an eight byte string. Finally, two eight-byte strings would be reused to create the desired new string of 16 bytes. Altogether four new strings had to be created, one of length 2, one of length 4, one of length 8 and one of length 16. The total cost is 2 + 4 + 8 + 16 = 30. In the long run this efficiency can be computed by adding in reverse order and using a geometric series starting with a first term a1 = N and having a common ratio of r = 1/2. The sum of a geometric series is given by a_1 / (1-r) = 2N. This is more efficient than adding one character to create a new string of length 2, creating a new string of length 3, 4, 5, and so on, until 16. The previous algorithm used that process of adding a single byte at a time, and the total cost of it would be n (n + 1) / 2 = 16 (17) / 2 = 8 (17) = 136. Obviously, 136 is a much greater number than 30, and so the previous algorithm takes much, much more time to build up a string. To compare the two methods you can see how much faster the recursive algorithm (also called "divide and conquer") is on a string of length 123,457. On my FreeBSD computer this algorithm, implemented in the stringFill3() function, creates the string in 0.001058 seconds, while the original stringFill1() function creates the string in 0.0808 seconds. The new function is 76 times faster. The difference in performance grows as the length of the string becomes larger. In the limit as larger and larger strings are created, the original function behaves roughly like C1 (constant) times N^2, and the new function behaves like C2 (constant) times N. From our experiment we can determine the value of C1 to be C1 = 0.0808 / (123457)2 = .00000000000530126997, and the value of C2 to be C2 = 0.001058 / 123457 = .00000000856978543136. In 10 seconds, the new function could create a string containing 1,166,890,359 characters. In order to create this same string, the old function would need 7,218,384 seconds of time. This is almost three months compared to ten seconds! I'm only answering (several years late) because my original solution to this problem has been floating around the Internet for more than 10 years, and apparently is still poorly-understood by the few who do remember it. I thought that by writing an article about it here I would help: Performance Optimizations for High Speed JavaScript / Page 3 Unfortunately, some of the other solutions presented here are still some of those that would take three months to produce the same amount of output that a proper solution creates in 10 seconds. I want to take the time to reproduce part of the article here as a canonical answer on Stack Overflow. Note that the best-performing algorithm here is clearly based on my algorithm and was probably inherited from someone else's 3rd or 4th generation adaptation. Unfortunately, the modifications resulted in reducing its performance. The variation of my solution presented here perhaps did not understand my confusing for (;;) expression which looks like the main infinite loop of a server written in C, and which was simply designed to allow a carefully-positioned break statement for loop control, the most compact way to avoid exponentially replicating the string one extra unnecessary time.
Good news! String.prototype.repeat is now a part of JavaScript. "yo".repeat(2); // returns: "yoyo" The method is supported by all major browsers, except Internet Explorer. For an up to date list, see MDN: String.prototype.repeat > Browser compatibility. MDN has a polyfill for browsers without support.
This one is pretty efficient String.prototype.repeat = function(times){ var result=""; var pattern=this; while (times > 0) { if (times&1) result+=pattern; times>>=1; pattern+=pattern; } return result; };
String.prototype.repeat is now ES6 Standard. 'abc'.repeat(3); //abcabcabc
Expanding P.Bailey's solution: String.prototype.repeat = function(num) { return new Array(isNaN(num)? 1 : ++num).join(this); } This way you should be safe from unexpected argument types: var foo = 'bar'; alert(foo.repeat(3)); // Will work, "barbarbar" alert(foo.repeat('3')); // Same as above alert(foo.repeat(true)); // Same as foo.repeat(1) alert(foo.repeat(0)); // This and all the following return an empty alert(foo.repeat(false)); // string while not causing an exception alert(foo.repeat(null)); alert(foo.repeat(undefined)); alert(foo.repeat({})); // Object alert(foo.repeat(function () {})); // Function EDIT: Credits to jerone for his elegant ++num idea!
Use Array(N+1).join("string_to_repeat")
Here's a 5-7% improvement on disfated's answer. Unroll the loop by stopping at count > 1 and perform an additional result += pattnern concat after the loop. This will avoid the loops final previously unused pattern += pattern without having to use an expensive if-check. The final result would look like this: String.prototype.repeat = function(count) { if (count < 1) return ''; var result = '', pattern = this.valueOf(); while (count > 1) { if (count & 1) result += pattern; count >>= 1, pattern += pattern; } result += pattern; return result; }; And here's disfated's fiddle forked for the unrolled version: http://jsfiddle.net/wsdfg/
/** #desc: repeat string #param: n - times #param: d - delimiter */ String.prototype.repeat = function (n, d) { return --n ? this + (d || '') + this.repeat(n, d) : '' + this }; this is how to repeat string several times using delimeter.
Tests of the various methods: var repeatMethods = { control: function (n,s) { /* all of these lines are common to all methods */ if (n==0) return ''; if (n==1 || isNaN(n)) return s; return ''; }, divideAndConquer: function (n, s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; with(Math) { return arguments.callee(floor(n/2), s)+arguments.callee(ceil(n/2), s); } }, linearRecurse: function (n,s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; return s+arguments.callee(--n, s); }, newArray: function (n, s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; return (new Array(isNaN(n) ? 1 : ++n)).join(s); }, fillAndJoin: function (n, s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; var ret = []; for (var i=0; i<n; i++) ret.push(s); return ret.join(''); }, concat: function (n,s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; var ret = ''; for (var i=0; i<n; i++) ret+=s; return ret; }, artistoex: function (n,s) { var result = ''; while (n>0) { if (n&1) result+=s; n>>=1, s+=s; }; return result; } }; function testNum(len, dev) { with(Math) { return round(len+1+dev*(random()-0.5)); } } function testString(len, dev) { return (new Array(testNum(len, dev))).join(' '); } var testTime = 1000, tests = { biggie: { str: { len: 25, dev: 12 }, rep: {len: 200, dev: 50 } }, smalls: { str: { len: 5, dev: 5}, rep: { len: 5, dev: 5 } } }; var testCount = 0; var winnar = null; var inflight = 0; for (var methodName in repeatMethods) { var method = repeatMethods[methodName]; for (var testName in tests) { testCount++; var test = tests[testName]; var testId = methodName+':'+testName; var result = { id: testId, testParams: test } result.count=0; (function (result) { inflight++; setTimeout(function () { result.start = +new Date(); while ((new Date() - result.start) < testTime) { method(testNum(test.rep.len, test.rep.dev), testString(test.str.len, test.str.dev)); result.count++; } result.end = +new Date(); result.rate = 1000*result.count/(result.end-result.start) console.log(result); if (winnar === null || winnar.rate < result.rate) winnar = result; inflight--; if (inflight==0) { console.log('The winner: '); console.log(winnar); } }, (100+testTime)*testCount); }(result)); } }
Here's the JSLint safe version String.prototype.repeat = function (num) { var a = []; a.length = num << 0 + 1; return a.join(this); };
For all browsers This is about as concise as it gets : function repeat(s, n) { return new Array(n+1).join(s); } If you also care about performance, this is a much better approach : function repeat(s, n) { var a=[],i=0;for(;i<n;)a[i++]=s;return a.join(''); } If you want to compare the performance of both options, see this Fiddle and this Fiddle for benchmark tests. During my own tests, the second option was about 2 times faster in Firefox and about 4 times faster in Chrome! For moderns browsers only : In modern browsers, you can now also do this : function repeat(s,n) { return s.repeat(n) }; This option is not only shorter than both other options, but it's even faster than the second option. Unfortunately, it doesn't work in any version of Internet explorer. The numbers in the table specify the first browser version that fully supports the method :
function repeat(pattern, count) { for (var result = '';;) { if (count & 1) { result += pattern; } if (count >>= 1) { pattern += pattern; } else { return result; } } } You can test it at JSFiddle. Benchmarked against the hacky Array.join and mine is, roughly speaking, 10 (Chrome) to 100 (Safari) to 200 (Firefox) times faster (depending on the browser).
Just another repeat function: function repeat(s, n) { var str = ''; for (var i = 0; i < n; i++) { str += s; } return str; }
There are many ways in the ES-Next ways 1. ES2015/ES6 has been realized this repeat() method! /** * str: String * count: Number */ const str = `hello repeat!\n`, count = 3; let resultString = str.repeat(count); console.log(`resultString = \n${resultString}`); /* resultString = hello repeat! hello repeat! hello repeat! */ ({ toString: () => 'abc', repeat: String.prototype.repeat }).repeat(2); // 'abcabc' (repeat() is a generic method) // Examples 'abc'.repeat(0); // '' 'abc'.repeat(1); // 'abc' 'abc'.repeat(2); // 'abcabc' 'abc'.repeat(3.5); // 'abcabcabc' (count will be converted to integer) // 'abc'.repeat(1/0); // RangeError // 'abc'.repeat(-1); // RangeError 2. ES2017/ES8 new add String.prototype.padStart() const str = 'abc '; const times = 3; const newStr = str.padStart(str.length * times, str.toUpperCase()); console.log(`newStr =`, newStr); // "newStr =" "ABC ABC abc " 3. ES2017/ES8 new add String.prototype.padEnd() const str = 'abc '; const times = 3; const newStr = str.padEnd(str.length * times, str.toUpperCase()); console.log(`newStr =`, newStr); // "newStr =" "abc ABC ABC " refs http://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.repeat https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
function repeat(s, n) { var r=""; for (var a=0;a<n;a++) r+=s; return r;}
This may be the smallest recursive one:- String.prototype.repeat = function(n,s) { s = s || "" if(n>0) { s += this s = this.repeat(--n,s) } return s}
Fiddle: http://jsfiddle.net/3Y9v2/ function repeat(s, n){ return ((new Array(n+1)).join(s)); } alert(repeat('R', 10));
Simple recursive concatenation I just wanted to give it a bash, and made this: function ditto( s, r, c ) { return c-- ? ditto( s, r += s, c ) : r; } ditto( "foo", "", 128 ); I can't say I gave it much thought, and it probably shows :-) This is arguably better String.prototype.ditto = function( c ) { return --c ? this + this.ditto( c ) : this; }; "foo".ditto( 128 ); And it's a lot like an answer already posted - I know this. But why be recursive at all? And how about a little default behaviour too? String.prototype.ditto = function() { var c = Number( arguments[ 0 ] ) || 2, r = this.valueOf(); while ( --c ) { r += this; } return r; } "foo".ditto(); Because, although the non recursive method will handle arbitrarily large repeats without hitting call stack limits, it's a lot slower. Why did I bother adding more methods that aren't half as clever as those already posted? Partly for my own amusement, and partly to point out in the simplest way I know that there are many ways to skin a cat, and depending on the situation, it's quite possible that the apparently best method isn't ideal. A relatively fast and sophisticated method may effectively crash and burn under certain circumstances, whilst a slower, simpler method may get the job done - eventually. Some methods may be little more than exploits, and as such prone to being fixed out of existence, and other methods may work beautifully in all conditions, but are so constructed that one simply has no idea how it works. "So what if I dunno how it works?!" Seriously? JavaScript suffers from one of its greatest strengths; it's highly tolerant of bad behaviour, and so flexible it'll bend over backwards to return results, when it might have been better for everyone if it'd snapped! "With great power, comes great responsibility" ;-) But more seriously and importantly, although general questions like this do lead to awesomeness in the form of clever answers that if nothing else, expand one's knowledge and horizons, in the end, the task at hand - the practical script that uses the resulting method - may require a little less, or a little more clever than is suggested. These "perfect" algorithms are fun and all, but "one size fits all" will rarely if ever be better than tailor made. This sermon was brought to you courtesy of a lack of sleep and a passing interest. Go forth and code!
Firstly, the OP's questions seems to be about conciseness - which I understand to mean "simple and easy to read", while most answers seem to be about efficiency - which is obviously not the same thing and also I think that unless you implement some very specific large data manipulating algorithms, shouldn't worry you when you come to implement basic data manipulation Javascript functions. Conciseness is much more important. Secondly, as André Laszlo noted, String.repeat is part of ECMAScript 6 and already available in several popular implementations - so the most concise implementation of String.repeat is not to implement it ;-) Lastly, if you need to support hosts that don't offer the ECMAScript 6 implementation, MDN's polyfill mentioned by André Laszlo is anything but concise. So, without further ado - here is my concise polyfill: String.prototype.repeat = String.prototype.repeat || function(n){ return n<=1 ? this : this.concat(this.repeat(n-1)); } Yes, this is a recursion. I like recursions - they are simple and if done correctly are easy to understand. Regarding efficiency, if the language supports it they can be very efficient if written correctly. From my tests, this method is ~60% faster than the Array.join approach. Although it obviously comes nowhere close disfated's implementation, it is much simpler than both. My test setup is node v0.10, using "Strict mode" (I think it enables some sort of TCO), calling repeat(1000) on a 10 character string a million times.
If you think all those prototype definitions, array creations, and join operations are overkill, just use a single line code where you need it. String S repeating N times: for (var i = 0, result = ''; i < N; i++) result += S;
Use Lodash for Javascript utility functionality, like repeating strings. Lodash provides nice performance and ECMAScript compatibility. I highly recommend it for UI development and it works well server side, too. Here's how to repeat the string "yo" 2 times using Lodash: > _.repeat('yo', 2) "yoyo"
Recursive solution using divide and conquer: function repeat(n, s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; with(Math) { return repeat(floor(n/2), s)+repeat(ceil(n/2), s); } }
I came here randomly and never had a reason to repeat a char in javascript before. I was impressed by artistoex's way of doing it and disfated's results. I noticed that the last string concat was unnecessary, as Dennis also pointed out. I noticed a few more things when playing with the sampling disfated put together. The results varied a fair amount often favoring the last run and similar algorithms would often jockey for position. One of the things I changed was instead of using the JSLitmus generated count as the seed for the calls; as count was generated different for the various methods, I put in an index. This made the thing much more reliable. I then looked at ensuring that varying sized strings were passed to the functions. This prevented some of the variations I saw, where some algorithms did better at the single chars or smaller strings. However the top 3 methods all did well regardless of the string size. Forked test set http://jsfiddle.net/schmide/fCqp3/134/ // repeated string var string = '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'; // count paremeter is changed on every test iteration, limit it's maximum value here var maxCount = 200; var n = 0; $.each(tests, function (name) { var fn = tests[name]; JSLitmus.test(++n + '. ' + name, function (count) { var index = 0; while (count--) { fn.call(string.slice(0, index % string.length), index % maxCount); index++; } }); if (fn.call('>', 10).length !== 10) $('body').prepend('<h1>Error in "' + name + '"</h1>'); }); JSLitmus.runAll(); I then included Dennis' fix and decided to see if I could find a way to eek out a bit more. Since javascript can't really optimize things, the best way to improve performance is to manually avoid things. If I took the first 4 trivial results out of the loop, I could avoid 2-4 string stores and write the final store directly to the result. // final: growing pattern + prototypejs check (count < 1) 'final avoid': function (count) { if (!count) return ''; if (count == 1) return this.valueOf(); var pattern = this.valueOf(); if (count == 2) return pattern + pattern; if (count == 3) return pattern + pattern + pattern; var result; if (count & 1) result = pattern; else result = ''; count >>= 1; do { pattern += pattern; if (count & 1) result += pattern; count >>= 1; } while (count > 1); return result + pattern + pattern; } This resulted in a 1-2% improvement on average over Dennis' fix. However, different runs and different browsers would show a fair enough variance that this extra code probably isn't worth the effort over the 2 previous algorithms. A chart Edit: I did this mostly under chrome. Firefox and IE will often favor Dennis by a couple %.
Simple method: String.prototype.repeat = function(num) { num = parseInt(num); if (num < 0) return ''; return new Array(num + 1).join(this); }
People overcomplicate this to a ridiculous extent or waste performance. Arrays? Recursion? You've got to be kidding me. function repeat (string, times) { var result = '' while (times-- > 0) result += string return result } Edit. I ran some simple tests to compare with the bitwise version posted by artistoex / disfated and a bunch of other people. The latter was only marginally faster, but orders of magnitude more memory-efficient. For 1000000 repeats of the word 'blah', the Node process went up to 46 megabytes with the simple concatenation algorithm (above), but only 5.5 megabytes with the logarithmic algorithm. The latter is definitely the way to go. Reposting it for the sake of clarity: function repeat (string, times) { var result = '' while (times > 0) { if (times & 1) result += string times >>= 1 string += string } return result }
Concatenating strings based on an number. function concatStr(str, num) { var arr = []; //Construct an array for (var i = 0; i < num; i++) arr[i] = str; //Join all elements str = arr.join(''); return str; } console.log(concatStr("abc", 3)); Hope that helps!
With ES8 you could also use padStart or padEnd for this. eg. var str = 'cat'; var num = 23; var size = str.length * num; "".padStart(size, str) // outputs: 'catcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcat'
To repeat a string in a specified number of times, we can use the built-in repeat() method in JavaScript. Here is an example that repeats the following string for 4 times: const name = "king"; const repeat = name.repeat(4); console.log(repeat); Output: "kingkingkingking" or we can create our own verison of repeat() function like this: function repeat(str, n) { if (!str || !n) { return; } let final = ""; while (n) { final += s; n--; } return final; } console.log(repeat("king", 3)) (originally posted at https://reactgo.com/javascript-repeat-string/)
Why are negative array indices much slower than positive array indices?
Here's a simple JavaScript performance test: const iterations = new Array(10 ** 7); var x = 0; var i = iterations.length + 1; console.time('negative'); while (--i) { x += iterations[-i]; } console.timeEnd('negative'); var y = 0; var j = iterations.length; console.time('positive'); while (j--) { y += iterations[j]; } console.timeEnd('positive'); The first loop counts from 10,000,000 down to 1 and accesses an array with a length of 10 million using a negative index on each iteration. So it goes through the array from beginning to end. The second loop counts from 9,999,999 down to 0 and accesses the same array using a positive index on each iteration. So it goes through the array in reverse. On my PC, the first loop takes longer than 6 seconds to complete, but the second one only takes ~400ms. Why is the second loop faster than the first?
Because iterations[-1] will evaluate to undefined (which is slow as it has to go up the whole prototype chain and can't take a fast path) also doing math with NaN will always be slow as it is the non common case. Also initializing iterations with numbers will make the whole test more useful. Pro Tip: If you try to compare the performance of two codes, they should both result in the same operation at the end ... Some general words about performance tests: Performance is the compiler's job these days, code optimized by the compiler will always be faster than code you are trying to optimize through some "tricks". Therefore you should write code that is likely by the compiler to optimize, and that is in every case, the code that everyone else writes (also your coworkers will love you if you do so). Optimizing that is the most benefitial from the engine's view. Therefore I'd write: let acc = 0; for(const value of array) acc += value; // or const acc = array.reduce((a, b) => a + b, 0); However in the end it's just a loop, you won't waste much time if the loop is performing bad, but you will if the whole algorithm performs bad (time complexity of O(n²) or more). Focus on the important things, not the loops.
To elaborate on Jonas Wilms' answer, Javascript does not work with negative indice (unlike languages like Python). iterations[-1] is equal to iteration["-1"], which look for the property named -1 in the array object. That's why iterations[-1] will evaluate to undefined.
Dealing with very big numbers in JS [duplicate]
I'm working on the Project Euler problems (currently question 13). For this question I have to find the first 10 digits of the sum of 100 numbers all of a size similar to this: 91,942,213,363,574,161,572,522,430,563,301,811,072,406,154,908,250 I think I could use something like Java's BigInteger, but I started solving the problems in JavaScript (I'm trying to boost my js abilities for work), and I would like to continue using it, even to solve this problem. I'd like to stick to pure JS if possible.
Javascript recently got a new primitive data type BigInt (stage 4 proposal as of January 2020). https://github.com/tc39/proposal-bigint Chrome, Firefox and few other browsers have started supporting this in newer versions (check compatibility here), while other browsers are still implementing it. https://developers.google.com/web/updates/2018/05/bigint Basically it can be declared using either literals like var a = 1n; or var b = BigInt('22222222222222222222222222222222'); Math operators don't do auto conversion between BigInt and Number, so 1 + 1n will throw an error.
You are going to need a javascript based BigInteger library. There are many to choose from. Here is one https://github.com/peterolson/BigInteger.js You can use it like this var n = bigInt("91942213363574161572522430563301811072406154908250") .plus("91942213363574161572522430563301811072406154908250");
Surprisingly, sticking all the values in an array and adding them all together and just taking the first 10 digits worked. I must have had a typo somewhere in my code when it didn't work before. I'm sure that doing something this simple wouldn't work in all cases (like those #AlexMcmillan and #zerkms have been debating about). I think the safest bet is the BigInteger library mentioned by #bhspencer, but it seems like adding the first x significant digits with y digits as a buffer might also be worth a shot in some cases.
I did this using an array and updating all entries with a function. function f(a) { for (let i = 0; i < a.length - 1; i++) { a[i + 1] = a[i + 1] + parseInt(a[i] / 10); a[i] = a[i] % 10; } return a; } // remember to init the array with enough elements for all digits var a = Array(200); a.fill(0); a[0] = 1; Here is a JSFiddle with the code for problem 20.
You could always convert your sum to a string, rip out the . and grab the result - something like this: var sum = 2384762348723648237462348; sum = sum.toString(); // "2.3847623487236483e+24" // Rip out the "." sum = sum.substr(0, 1) + sum.substr(2); // Grab the first 10 characters var firstTen = sum.substr(0, 10);
Where should I find how the indexOf function is written for JavaScript core looks like?
I want to see it because there is an algorithm interview question asked similar thing but no one was able to resolve it without some sort of internal function like indexOf() which would have been perfectly fine except that the requirement was NOT to use an internal function to resolve this. Since I think the best implementation is already there, why don't I just learn from the best? Which I suppose is how they wrote .indexOf() for String already in JavaScript. My solution was humiliating to even show it... so I decide not to, in other words, there are way too much conditional toggling than I would want to put in there. Thanks Question is following Avoid using built-in functions to solve this challenge. Implement them yourself, since this is what you would be asked to do during a real interview. Implement a function that takes two strings, s and x, as arguments and finds the first occurrence of the string x in s. The function should return an integer indicating the index in s of the first occurrence of x. If there are no occurrences of x in s, return -1. Example For s = "CodefightsIsAwesome" and x = "IA", the output should be strstr(s, x) = -1; For s = "CodefightsIsAwesome" and x = "IsA", the output should be strstr(s, x) = 10. Input/Output [time limit] 4000ms (js) [input] string s A string containing only uppercase or lowercase English letters. Guaranteed constraints: 1 ≤ s.length ≤ 106. [input] string x String, containing only uppercase or lowercase English letters. Guaranteed constraints: 1 ≤ x.length ≤ 106. [output] integer An integer indicating the index of the first occurrence of the string x in s, or -1 if s does not contain x Bitw its from codefight https://codefights.com/interview-practice/task/C8Jdyk3ybixqQdAvM For anyone interested to test their code in full with all test cases, its here (see where my cursor is pointing to in the image vvv?) https://codefights.com/interview-practice/topics/strings
no one was able to resolve it I'm a little shocked that was the case, as it's just 2 simple nested for loops. Eg, it took me less than a few minutes to knock this up. function strstr(a,b) { for (var o = 0; a[o]; o ++) { var found = true; for (var i = 0; b[i]; i ++) { if (a[o + i] !== b[i]) { found=false; break; } } if (found) return o; } return -1; } var s = "CodefightsIsAwesome"; var x = "IA"; console.log(strstr(s, x)); s = "CodefightsIsAwesome"; x = "IsA"; console.log(strstr(s, x));
JavaScript runs on ECMAScript Engines. ECMAScript is nothing but the specification of JavaScript. Different browser provides different implementation for that, like Chrome has V8 engine implementation for ECMAScript, similarly Carakan for Opera. Source So with that in mind... Here you might get some more information on how things work: ECMA Specification General MDN entry: Click
Why is implicit casting is much faster than explicit in JS? Is implicit casting a good practice?
I know it is cleaner and nicer to cast types like String(1234) and Number("1234"), but I just tried to benchmark alternative ways of doing the same thing, specifically "" + 1234 // -> "1234" and - - "1234" // -> 1234. The results were quite surprising (for me). I iterated over each way 100,000,000 times in Chrome. I used this simple code. var then = Date.now(); for (var i = 0; i < 100000000; ++i) { var a = - - "1234"; }; console.log(Date.now() - then); Number("1234") took 2351 ms whereas - - "1234" took only 748 ms. Similarly for the other way around, String(1234) took 3701 ms whereas "" + 1234 took only 893 ms. The difference is surprisingly huge. My questions are: What makes the explicit casting so much slower than implicit? My intuition tells me it should be the other way around. Is it a good practice to use implicit casting? Especially the hacky - - "1234"? Are there nicer alternatives? PS: I just tried the same in Firefox. It was about 500 times slower (but still the implicit conversion was much faster). What is going on? Is it connected to branch prediction or something similar? I guess I am benchmarking wrong.
If you instead of using a constant, if you use i instant then the result will be a quite different: console.time('a'); for (var i = 0; i < 1e7; ++i) { var a = String(i); }; console.timeEnd('a'); console.time('b'); for (var i = 0; i < 1e7; ++i) { var a = "" + i; }; console.timeEnd('b'); Output: a: 1062.192ms b: 884.535ms Note I have to remove a power of 10 as well. 100000000 === 1e8 And I uses 1e7. This suggests that there is happening a lot of optimization under the hood when working with a constant as in your benchmark. And now Number(...) seems to be faster: console.time('a'); for (var i = 0; i < 1e7; ++i) { var a = - - ("" + i); }; console.timeEnd('a'); console.time('b'); for (var i = 0; i < 1e7; ++i) { var a = Number("" + i); }; console.timeEnd('b'); Output: a: 2010.903ms b: 1557.735ms
In theory, use of the unary + and - operators should be faster than calling Number and String since they use the internal ToNumber and ToString methods to convert the operands to number Type, whereas Number and String require the additional overhead of a function call. However, theory doesn't always match practice as it's likely very simple to optimise Number(x) to +x, or vice versa, which ever the compiler thinks is faster. What makes the explicit casting so much slower than implicit? My intuition tells me it should be the other way around. As always, the results you get in a particular version of a browser don't necessarily apply to other browsers or even other versions of the same browser. In theory, explicit conversion should be slower, but I wouldn't depend on that across implementations. Is it a good practice to use implicit casting? Especially the hacky - - "1234"? Are there nicer alternatives? That should be -'1234' and I would say "no" since the - operator converts it's argument to Number anyway, there is never a need to write x - -y. It's much more common to use unary + for conversion in conjunction with the addition operator +, and in most circumstances it's equally clear to write +x or Number(x). So use: x + +y and save some typing.