TL;DR
In theory, an unoptimized version of this loop:
for (let i = 0; i < 500; ++i) {
doSomethingWith(i);
}
might be slower than an unoptimized version of the same loop with var:
for (var i = 0; i < 500; ++i) {
doSomethingWith(i);
}
because a different i variable is created for each loop iteration with let, whereas there's only one i with var.
Arguing against that is the fact the var is hoisted so it's declared outside the loop whereas the let is only declared within the loop, which may offer an optimization advantage.
In practice, here in 2018, modern JavaScript engines do enough introspection of the loop to know when it can optimize that difference away. (Even before then, odds are your loop was doing enough work that the additional let-related overhead was washed out anyway. But now you don't even have to worry about it.)
Beware synthetic benchmarks as they are extremely easy to get wrong, and trigger JavaScript engine optimizers in ways that real code doesn't (both good and bad ways). However, if you want a synthetic benchmark, here's one:
const now = typeof performance === "object" && performance.now
? performance.now.bind(performance)
: Date.now.bind(Date);
const btn = document.getElementById("btn");
btn.addEventListener("click", function() {
btn.disabled = true;
runTest();
});
const maxTests = 100;
const loopLimit = 50000000;
const expectedX = 1249999975000000;
function runTest(index = 1, results = {usingVar: 0, usingLet: 0}) {
console.log(`Running Test #${index} of ${maxTests}`);
setTimeout(() => {
const varTime = usingVar();
const letTime = usingLet();
results.usingVar += varTime;
results.usingLet += letTime;
console.log(`Test ${index}: var = ${varTime}ms, let = ${letTime}ms`);
++index;
if (index <= maxTests) {
setTimeout(() => runTest(index, results), 0);
} else {
console.log(`Average time with var: ${(results.usingVar / maxTests).toFixed(2)}ms`);
console.log(`Average time with let: ${(results.usingLet / maxTests).toFixed(2)}ms`);
btn.disabled = false;
}
}, 0);
}
function usingVar() {
const start = now();
let x = 0;
for (var i = 0; i < loopLimit; i++) {
x += i;
}
if (x !== expectedX) {
throw new Error("Error in test");
}
return now() - start;
}
function usingLet() {
const start = now();
let x = 0;
for (let i = 0; i < loopLimit; i++) {
x += i;
}
if (x !== expectedX) {
throw new Error("Error in test");
}
return now() - start;
}
<input id="btn" type="button" value="Start">
It says that there's no significant difference in that synthetic test on either V8/Chrome or SpiderMonkey/Firefox. (Repeated tests in both browsers have one winning, or the other winning, and in both cases within a margin of error.) But again, it's a synthetic benchmark, not your code. Worry about the performance of your code when and if your code has a performance problem.
As a style matter, I prefer let for the scoping benefit and the closure-in-loops benefit if I use the loop variable in a closure.
Details
The important difference between var and let in a for loop is that a different i is created for each iteration; it addresses the classic "closures in loop" problem:
function usingVar() {
for (var i = 0; i < 3; ++i) {
setTimeout(function() {
console.log("var's i: " + i);
}, 0);
}
}
function usingLet() {
for (let i = 0; i < 3; ++i) {
setTimeout(function() {
console.log("let's i: " + i);
}, 0);
}
}
usingVar();
setTimeout(usingLet, 20);
Creating the new EnvironmentRecord for each loop body (spec link) is work, and work takes time, which is why in theory the let version is slower than the var version.
But the difference only matters if you create a function (closure) within the loop that uses i, as I did in that runnable snippet example above. Otherwise, the distinction can't be observed and can be optimized away.
Here in 2018, it looks like V8 (and SpiderMonkey in Firefox) is doing sufficient introspection that there's no performance cost in a loop that doesn't make use of let's variable-per-iteration semantics. See this test.
In some cases, const may well provide an opportunity for optimization that var wouldn't, especially for global variables.
The problem with a global variable is that it's, well, global; any code anywhere could access it. So if you declare a variable with var that you never intend to change (and never do change in your code), the engine can't assume it's never going to change as the result of code loaded later or similar.
With const, though, you're explicitly telling the engine that the value cannot change¹. So it's free to do any optimization it wants, including emitting a literal instead of a variable reference to code using it, knowing that the values cannot be changed.
¹ Remember that with objects, the value is a reference to the object, not the object itself. So with const o = {}, you could change the state of the object (o.answer = 42), but you can't make o point to a new object (because that would require changing the object reference it contains).
When using let or const in other var-like situations, they're not likely to have different performance. This function should have exactly the same performance whether you use var or let, for instance:
function foo() {
var i = 0;
while (Math.random() < 0.5) {
++i;
}
return i;
}
It's all, of course, unlikely to matter and something to worry about only if and when there's a real problem to solve.
Answer from T.J. Crowder on Stack OverflowTL;DR
In theory, an unoptimized version of this loop:
for (let i = 0; i < 500; ++i) {
doSomethingWith(i);
}
might be slower than an unoptimized version of the same loop with var:
for (var i = 0; i < 500; ++i) {
doSomethingWith(i);
}
because a different i variable is created for each loop iteration with let, whereas there's only one i with var.
Arguing against that is the fact the var is hoisted so it's declared outside the loop whereas the let is only declared within the loop, which may offer an optimization advantage.
In practice, here in 2018, modern JavaScript engines do enough introspection of the loop to know when it can optimize that difference away. (Even before then, odds are your loop was doing enough work that the additional let-related overhead was washed out anyway. But now you don't even have to worry about it.)
Beware synthetic benchmarks as they are extremely easy to get wrong, and trigger JavaScript engine optimizers in ways that real code doesn't (both good and bad ways). However, if you want a synthetic benchmark, here's one:
const now = typeof performance === "object" && performance.now
? performance.now.bind(performance)
: Date.now.bind(Date);
const btn = document.getElementById("btn");
btn.addEventListener("click", function() {
btn.disabled = true;
runTest();
});
const maxTests = 100;
const loopLimit = 50000000;
const expectedX = 1249999975000000;
function runTest(index = 1, results = {usingVar: 0, usingLet: 0}) {
console.log(`Running Test #${index} of ${maxTests}`);
setTimeout(() => {
const varTime = usingVar();
const letTime = usingLet();
results.usingVar += varTime;
results.usingLet += letTime;
console.log(`Test ${index}: var = ${varTime}ms, let = ${letTime}ms`);
++index;
if (index <= maxTests) {
setTimeout(() => runTest(index, results), 0);
} else {
console.log(`Average time with var: ${(results.usingVar / maxTests).toFixed(2)}ms`);
console.log(`Average time with let: ${(results.usingLet / maxTests).toFixed(2)}ms`);
btn.disabled = false;
}
}, 0);
}
function usingVar() {
const start = now();
let x = 0;
for (var i = 0; i < loopLimit; i++) {
x += i;
}
if (x !== expectedX) {
throw new Error("Error in test");
}
return now() - start;
}
function usingLet() {
const start = now();
let x = 0;
for (let i = 0; i < loopLimit; i++) {
x += i;
}
if (x !== expectedX) {
throw new Error("Error in test");
}
return now() - start;
}
<input id="btn" type="button" value="Start">
It says that there's no significant difference in that synthetic test on either V8/Chrome or SpiderMonkey/Firefox. (Repeated tests in both browsers have one winning, or the other winning, and in both cases within a margin of error.) But again, it's a synthetic benchmark, not your code. Worry about the performance of your code when and if your code has a performance problem.
As a style matter, I prefer let for the scoping benefit and the closure-in-loops benefit if I use the loop variable in a closure.
Details
The important difference between var and let in a for loop is that a different i is created for each iteration; it addresses the classic "closures in loop" problem:
function usingVar() {
for (var i = 0; i < 3; ++i) {
setTimeout(function() {
console.log("var's i: " + i);
}, 0);
}
}
function usingLet() {
for (let i = 0; i < 3; ++i) {
setTimeout(function() {
console.log("let's i: " + i);
}, 0);
}
}
usingVar();
setTimeout(usingLet, 20);
Creating the new EnvironmentRecord for each loop body (spec link) is work, and work takes time, which is why in theory the let version is slower than the var version.
But the difference only matters if you create a function (closure) within the loop that uses i, as I did in that runnable snippet example above. Otherwise, the distinction can't be observed and can be optimized away.
Here in 2018, it looks like V8 (and SpiderMonkey in Firefox) is doing sufficient introspection that there's no performance cost in a loop that doesn't make use of let's variable-per-iteration semantics. See this test.
In some cases, const may well provide an opportunity for optimization that var wouldn't, especially for global variables.
The problem with a global variable is that it's, well, global; any code anywhere could access it. So if you declare a variable with var that you never intend to change (and never do change in your code), the engine can't assume it's never going to change as the result of code loaded later or similar.
With const, though, you're explicitly telling the engine that the value cannot change¹. So it's free to do any optimization it wants, including emitting a literal instead of a variable reference to code using it, knowing that the values cannot be changed.
¹ Remember that with objects, the value is a reference to the object, not the object itself. So with const o = {}, you could change the state of the object (o.answer = 42), but you can't make o point to a new object (because that would require changing the object reference it contains).
When using let or const in other var-like situations, they're not likely to have different performance. This function should have exactly the same performance whether you use var or let, for instance:
function foo() {
var i = 0;
while (Math.random() < 0.5) {
++i;
}
return i;
}
It's all, of course, unlikely to matter and something to worry about only if and when there's a real problem to solve.
"LET" IS BETTER IN LOOP DECLARATIONS
With a simple test (5 times) in navigator like that:
// WITH VAR
console.time("var-time")
for(var i = 0; i < 500000; i++){}
console.timeEnd("var-time")
The mean time to execute is more than 2.5ms
// WITH LET
console.time("let-time")
for(let i = 0; i < 500000; i++){}
console.timeEnd("let-time")
The mean time to execute is more than 1.5ms
I found that loop time with let is better.
Videos
It seems that the general consensus of people writing ES6 is to use const for variables that won't change in value, which principally I can't disagree with.
But sometimes I wonder how much that really gains your application from a performance standpoint. If someone tries to reassign a const somewhere in the app that truly shouldn't be reassigned, the value there is clear. But if we're talking in terms of pure processing speed, does it make a difference?
Let's say in the worst-case scenario, I replaced all instances of const in your app with let (or even var), and run a million iterations of the most complex function affected by this. What would change? Would it run any slower? Any difference for server-side code vs. client-side?
Just wondering this from a curiosity standpoint. I'm not against const in any way, would just like to know what it gains you besides the type of bug prevention it can possibly give in other programming languages.
Performance-wise, if whatever difference between let and const actually surfaces with meaningful impact then you're probably already in a very good spot.
const, however, is great for code readability because it is a statement of how the code is supposed to work. If I see a const variable I expect it not to be reassigned. If I see a let, I expect it to be reassigned. Anything else tells me there is likely a bug. Given that code clarity is almost always the greatest concern, using const as the default choice and let only when necessary makes a lot of sense to me.
It seems that the general consensus of people writing ES6 is to use const for variables that won't change in value, which principally I can't disagree with.
This is true. And it's a fairly good idea. (The benefits can be overstated. Keep in mind that const does not prevent, nor is it intended to prevent, people mutating complex values. const user = getUser(); user.name = 'Tom' is perfectly legal.)
But sometimes I wonder how much that really gains your application from a performance standpoint.
Absolutely, literally, nothing. That's not why people recommend it. I'd say that the benefits of const are basically 100% that it makes the code easier to write and (especially maintain) via marginal improvements to clarity and programmer intent. let tells me that the author is intending the re-assign a variable, which is good to know since that's pretty unusual. const tells me it's a "normal" variable that shouldn't be re-assigned, and my linter will helpfully flag it should I start to try (or if I try and re-declare it). Every so often I'll go "oh right, I already have a user object", and that's slightly helpful, and has no drawbacks, so...win?
I replaced all instances of const in your app with let (or even var), and run a million iterations of the most complex function affected by this. What would change? Would it run any slower?
Swap with let, and nothing would change at all; it certainly wouldn't run slower. Swap with var, and I suppose the different scoping might cause a bug and due to a couple of minor bugs in some browsers, it might even run faster.
My reply here is not javascript-specific.
As a rule of thumb in any language that lets me do so in a semi-easy way I'd say always use const/final/readonly/whatever it is called in your language whenever possible. The reason is simple, it's much easier to reason about code when it is dead obvious what can change and what cannot change. And in addition to this, in many languages you can get tool support that tells you that you are doing something wrong when you accidentially assign to a variable that you've declared as const.
Going back and changing a const to a let is dead simple. And going const by default makes you think twice before doing so. And this is in many cases a good thing.
How many bugs have you seen that involved variables changing unexpectedly? I'd guess a lot. I know that the majority of bugs that I see involve unexpected state changes. You won't get rid of all of these bugs by liberally using const, but you will get rid of a lot of them!
Also, many functional languages have immutable variables where all variables are const by default. Look at Erlang for example, or F#. Coding without assignment works perfectly in these languages and is one of the many reasons why people love functional programming. There is a lot to learn from these languages about managing state in order to become a better programmer.
And it all starts with being extremely liberal with const! ;) It's just two more characters to write compared to let, so go ahead and const all the things!
Be careful, because const object keys are mutable.
From here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
object keys are not protected
consider this example:
const colors = {red: "#f00"};
console.log(colors); // { "red": "#f00" }
colors.red = "#00f";
colors.green = "#0f0";
console.log(colors); // { "red": "#00f", "green": "#0f0" }
Same thing for arrays:
const numbers = [1, 2, 3];
console.log(numbers); // [ 1, 2, 3 ]
numbers.push(4);
console.log(numbers); // [ 1, 2, 3, 4 ]
I haven't decided totally myself, but I'm considering using const for all non-array/non-objects and use let for objects/arrays.
For context, I decided to make a coding channel recently. I made a video explaining why var is discouraged and why let/const is a better alternative.
A commenter left this on my post, I've translated it into English:
Translate it yourself from my language! When using const or let, allocators and scopes will be checked every time where you use them.
This is not significant for variables < 10000, but more - you will waste seconds of time on this stupid concept with let and const. You either write the code correctly, quickly and efficiently, or don't bully people about the fact that it's better to use let or const.
Moreover, const is not a constant, go learn the base.
I researched this and came to differing conclusions.
I would love some feedback!
Thank you! 🙏
Note: I wasn't rude in my video (or bullying as the guys says it), I'm assuming he took it the wrong way.
Basically,
- use
letif the variable's value will change during the code - use
constif it won't and you / your team want to useconstin those situations in the project you're working on; it's a matter of style
If you do use const, then it's surprising how often it turns out that the guidelines above mean you use const because you end up not needing to change a variable's value (if you're following the usual rules of keeping your functions of reasonable size and such). (Well, it surprised me, anyway...)
Using const when the variable's¹ value is not meant to change accomplishes a few things:
It tells others reading your code that you don't intend the value to change.
It gives you a nice proactive error if you change the code so it writes to that variable. (A decent IDE can flag this up proactively, but if not, you'll get the error when running the code.) You can then make an informed, intentional decision: Should you change it to
let, or did you not mean to change that variable's value in the first place?It gives a hint to the JavaScript engine's optimizer that you won't be changing that variable's value. While the engine can frequently work that out through code analysis, using
constsaves it the trouble. (Caveat: I have no idea whether this is actually useful to the JavaScript engine. It seems like it would be, but runtime code optimization is a very complicated and sometimes non-intuitive process.)
¹ Yes, it's funny to use the term "variable" to refer to something that by definition doesn't vary. :-) The specification's term is "binding," but I bet you won't hear people talking about "bindings" in everyday conversation anytime soon... So the aggregate term will probably remain "variable" except when we can specifically refer to something as a "constant."
Using const by default is essentially a matter of programming style. However there are pros and cons to each:
Arguments in favor of using const by default
The arguments in favor of using const by default are the following:
- It avoids side effects caused by involuntary reassignments;
- During a code review, it removes an uncertainty, because the developer who sees a
constvariable can count on the certainty that it will not be reassigned; - Maybe we could say it is more consistant with functional programming and immutable states.
- With TypeScript, there are some cases with better inferences.
Here is an example of advantage when using const with TypeScript:
const hello = "Hello" as string | undefined
if (hello !== undefined) {
["A", "B"].forEach(
name => console.log(`${hello.toUpperCase()}, ${name}`) // OK
)
}
With let, in strict mode, TypeScript detects an error:
let hello = // …
// …
name => console.log(`${hello.toUpperCase()}, ${name}`)
// ^__ error here: Object is possibly 'undefined'.
Arguments in favor of using let by default
I summarize here the article Use “let” by default, not “const” that gives arguments in favor of using let by default rather than const:
- Re-assignment is not a dangerous thing, it is just... usual;
- If a variable could be reassigned, then it should be declared with
let, because it is more expressive to reserveconstfor real constants; constis misleading because it doesn’t stop references being modified;- It is two more characters to write and to bundle;
- Using
constby default is inconsistant with function parameters; - There is no performance gain to use
const.