302

Babel's guide to ES6 says:

let is the new var.

Apparently the only difference is that var gets scoped to the current function, while let gets scoped to the current block. There are some good examples in this answer.

I can't see any reason to use var in ES6 code. Even if you want to scope a given variable to the whole function, you can do so with let by putting the declaration at the top of the function block, which is what you should be doing with var anyway to indicate the actual scope. And if you want to scope something more finely in an for block or something, then you can do that too.

So my instinct is to stop using var altogether when writing ES6 code.

My question is, am I wrong about this? Is there any legitimate case where var would be preferable over let?

callum
  • 10,377
  • 9
  • 30
  • 33
  • 3
    I haven't tried this yet (since I'm not yet writing ES6 code), but it seems that using `var` as a conscious indicator that this variable is intended to be scoped to the entire function might be a useful "self-documenting" convention. – jfriend00 Feb 24 '15 at 22:27
  • 13
    If you put a `let` statement right at the top of a function, I think it's just as obvious that you intended to scope it to the entire function. I don't think using `var` makes it any clearer than simply locating it at the top. – callum Feb 25 '15 at 13:47
  • 18
    Honestly, I believe that the only reason `var` still exists, is backwards-compatibility. If it weren't for that, they'd have removed `var` altogether, or never introduced `let` in the first place, instead changing the semantics of `var` to what it arguably should have been all along. – Jörg W Mittag Feb 25 '15 at 16:24
  • [This article](http://davidwalsh.name/for-and-against-let) makes some good cases for why we should use both `var` and `let`. – Ray Toal Jun 20 '15 at 21:31
  • 2
    @RayToal I agree with 97% of what Kyle Simpson says, but his reasons to keep using `var` seem thin to me, and not enough to warrant having a third kind of variable that jumps around. You can scope a `let` to a whole function simply by placing it at the top of the function, which is much clearer in intent than writing `var` in a block (in order to have it hoisted out of that block so you can then use it outside the block - weird). He warns that if you scope a `let` to a function then "it's just position that signals the difference, rather than syntax", but I think that's a good thing. – callum Jun 24 '15 at 15:33
  • 2
    @RayToal I too read that article (just before reading this discussion) and I was really disappointed for his very weak case for `var`. the examples he presents for keeping `var` seem contrived -- and are based on *severe* coding errors. It's much better to run into an error and be forced to fix such errors than to use language functions that let one get away with it! What's next, advise to wrap everything in an try/catch to prevent crashes? The rest of that link is good but I don't agree at all with that particular part. – Mörre Jul 01 '15 at 11:58
  • I agree, too, and exclusively use `let`. The article is at least a summary of why some programmers might want to use `var` (as some will undoubtably agree with Simpson) and seemed like it would be of interest to the OP. The addition of `let` was a good thing, but man, JavaScript has really gotten complex (`var`, `let`, `const`, and .... heck even `function` introduces a variable)! – Ray Toal Jul 02 '15 at 03:04
  • 2
    This is not much of an argument, but I just mass search and replaced all my vars with lets in some pretty complex TypeScript code, and had no problems. I can not imagine why I would ever use var in the future. – bnieland Feb 06 '16 at 21:32
  • 1
    I found a couple of use cases for var: if some variable needs to be declared by condition or if we need to use last value of a variable from the loop. Both of them are solved with let, but sometimes it is just easier to use var. – dy_ Aug 29 '16 at 14:18
  • `let` is not the new `var`! I see this mistake way too often. People are changing `var` to `let` in bulk instead of thinking. `const` should be used by default and `let` ONLY when the reference is overwritten later on. – Pawel Dec 21 '16 at 10:47
  • Variables declared with let are not hoisted to the top of the current execution context. In this example: var x = x || {}, if x does not yet exist, var works but let will not. I think let is useful for the most part because it limits scope, but it is not a complete replacement to var. It's good to have options. – tony Aug 08 '17 at 12:39
  • @Pawel I agree that `const` should be used whenever possible, but do you _really_ use so many constants that it should be the _default_? I have _always_ defined constants at the top of my code, in whatever language, and whether or not the language supports constants. I never have as many constants as assignable variables. – Auspex May 10 '19 at 09:26

5 Answers5

244

Doug Crockford discusses let at this point in his talk, "The Better Parts".

The point is, let avoids a source of misunderstanding, esp. for programmers with expectations set by languages with block-scope. A var has function scope (it declares a variable that's visible throughout the function) even though it looks like it has block scope.

var might possibly still be useful in an extreme case like machine-generated code, but I'm stretching hard there.

(const is also new and has block scope. After let x = {'hi': 'SE'} you can reassign to x, while after const y = x you cannot reassign to y. That's often preferrable since it keeps something from accidentally changing out from under you. But to be clear, you can still modify the object y.hi = 'SO' unless you freeze it.)

Realistically, your impression is right on for ES6: Adopt let and const. Stop using var.

(In another performance of "The Better Parts", Doug says why === was added rather than fixing the problems of ==. == produces some "surprising" results, so just adopt ===.)


A Revealing Example

Mozilla Developer Network gives an example where var does not work as intended. Their example is a realistic one that sets onclick handlers in a web page. Here's a smaller test case:

var a = [];
(function () {
   'use strict';
   for (let i = 0; i < 5; ++i) { // *** `let` works as expected ***
     a.push( function() {return i;} );
   }
} ());
console.log(a.map( function(f) {return f();} ));
// prints [0, 1, 2, 3, 4]

// Start over, but change `let` to `var`.
// prints [5, 5, 5, 5, 5]

var trips us up because all loop iterations share the same function-scoped i variable, which has the value 5 after the loop finishes.


Another Telling Example

function f(x) {
    let y = 1;
    if (x > 0) {
        let y = 2;  // `let` declares a variable in this block
    }
    return y;
}
[f(1), f(-1)]  // --> [1, 1]

// Start over, but change `let` to `var`.
// --> [2, 1]

let declares block-scoped variables. var confuses us by referring to the same variable throughout the function.

Jerry101
  • 5,367
  • 1
  • 15
  • 19
  • 7
    *He gives the same answer about adopting === instead of ==. The latter is broken but the ES standards committee didn't want to change it, so they added ===* Not quite sure what that means. `==` is not broken at all. It can simply be defined as `equality comparison using coersion` Check out https://github.com/getify/You-Dont-Know-JS/blob/master/types%20&%20grammar/ch4.md#loose-equals-vs-strict-equals – AmmarCSE May 12 '16 at 03:53
  • 19
    @AmmarCSE that's a wonderful 39 page doc on implicit coercions. Who can keep all that in mind while programming? Doug meant "broken" as summarized in http://stackoverflow.com/a/359509/1682419 , in short, easy to get bit when value types vary more than we presumed. Can you predict all these? `[ '1.0' == 1.0, [1.0] == 1.0, [1.0] == '1.0', ['1.0'] == 1.0, [null] == '', [null] == 'null', '00' == false, [] == [], [] == 0, [] == '', [] == false, [] == true, [010] - [4] == ' 4.0 ', !![0], !![1], [0] == true, [1] == true, 1 == [[1]], 0 == [[0]], '1' == [1] ]` – Jerry101 May 12 '16 at 06:54
  • 2
    right, most of those examples involve an array operand. It is easy to understand the result when you look into [toString](https://github.com/getify/You-Dont-Know-JS/blob/master/types%20&%20grammar/ch4.md#tostring) and how its implemented for *Objects*. However, if that is what is meant by *broken* then I completely see where that is coming from. :-) – AmmarCSE May 12 '16 at 07:53
  • 3
    why not use const whenever it's not mutable - just asking? I mean, the real choice it's not between `let` and `var` but rather between `let`, `var` and `const` – shabunc Jun 30 '16 at 22:03
  • @shabunc Yes! Good point. When deciding a name that you don't want to reassign, use `const`. It has block scope just like `let`. (Note that it doesn't make the value immutable, just the name binding.) – Jerry101 Jul 01 '16 at 01:29
  • 1
    A good programmer knows when to use var,let == vs ===, i.e to check whether a textfield had some value and wether that value is bigger than zero you can just use if (val == 0) or you can if(val === '' || val === 0). so choose wisely. – Yasin Yaqoobi Jul 17 '16 at 03:21
  • The youtube link ["The Better Parts"](http://youtu.be/vJKDh4UEXhw) is broken :( – caramba Dec 02 '16 at 07:06
  • @YasinYaqoobi `Number(val) === 0` is much clearer. – Aluan Haddad Apr 11 '17 at 00:47
  • 1
    Your revealing example is funny. Var works exactly as expected. It simply has different logic than block scoping. That does not mean it is wrong... – inf3rno Apr 26 '17 at 00:10
  • 6
    `var` works as designed, but not how many people expect. It's a usability bug, not an implementation bug. – Jerry101 Apr 26 '17 at 00:37
  • I'm using Matomo and it requires me to shove in `var x=x||[]`. I can't use `let` nor `const` at that stage. Is it **in this case** recommended to use all three keywords in the same script? Or are there any better approaches? – Konrad Viltersten Oct 09 '20 at 09:25
  • @KonradViltersten Please ask a new question about this case and include a minimal reproducible test case. Is there already an `x` in scope, e.g. a function parameter? What happens in environments besides Matomo? It's fine to use `var` as a workaround to a problem. – Jerry101 Oct 09 '20 at 16:24
23

If you have been writing correct code, you will probably be able to turn all var statements into let statements without any semantic changes.

let is preferable to var because it reduces the scope in which an identifier is visible. It allows us to safely declare variables at the site of first use.

const is preferable to let. Unless you need to mutate a reference, use a const declaration. This has all the benefits of let, along with reducing the presence of uninitialized variables and making code generally easier to reason about. If you aren't sure if you will need to mutate a reference, declare it const until you find yourself explicitly needing to mutate it, then declare it as let.

auspicious99
  • 107
  • 1
  • 8
Aluan Haddad
  • 678
  • 4
  • 9
5

I don't necessarily think you're wrong but there are caveats to using var. Essentially, let should help developers get around JavaScript's stupidity, particularly with naming conflicts. var, it seems, has a larger scope since it wants to go to the closes function scope. There will be times when you need var, like when you need a temp variable to be available within the scope of a block inside of a function, otherwise, preferring let over var will help developers with naming conflicts. On a lighter note, it's about time that ES6 introduced let.

curiousdork
  • 167
  • 2
  • 4
    A temp `var` in a block is available inside the entire function, not block-scoped. It's a misleading feature. – Jerry101 Feb 24 '15 at 23:06
  • For instances where I need to set a flag variable and check for it inside a block, `var` is what I still use. I do wonder though, if this is not the best way. Using `let` doesn't quite work though. – Qasim Mar 02 '21 at 06:47
2

I tend to agree that only "let" should be used in es6. AFIK, redeclaring a "let" generates an error (which is good), while with "var", you simply override the value (though "strict mode" in es5 takes care of that too).

bnieland
  • 89
  • 7
yar1
  • 169
  • 4
-5

let means "let variable equal". It's a declaration, in other words, an initialization and assignment.

It exists in contrast to const which of course means "constant" -- which is the opposite of variable.

Some other languages use a prefix to the actual object instead of the object when declaring it (e.g. def is a shorthand for "define function" -- completely missing the point of what is being "def".

Let adds this semantic inconsistency to Javascript.

Logically, you could let a constant equal something as well, since the job of the "let" keyword is to assign memory.

The problem arises because the var keyword was introduced before const was supported, so backwards compatibility dictates that var doesn't necessary mean a variable. (It could also be used to assign a constant value.)

Hence, the introduction of let in the wrong position. To make sure that we remember that lexically, this is wrong, they also decided to change the lexical scope of let vs var, so the inconsistency is foremost in our minds when debugging.

In other words, let exists because people (meaning the language maintainers) thought that Javascript was too consistent, having mastered all it's idioms and idiosyncrasies, desire more.

Side note, var does not work in "arrow" blocks if you want to treat said blocks as closures (because var was introduced before treating blocks as closures), but let does.

fijiaaron
  • 123
  • 2
  • 9
    -1: pedantic, unhelpful, and primarily opinion-based. – Joel Mueller Apr 26 '16 at 23:07
  • fijiaaron is just joking with us here. – Jerry101 May 12 '16 at 05:41
  • I am sympathetic to your dislike of the lexical form `let` because it is at odds with the tone of other keywords in the same category in JavaScript. Still this does not answer the question, and is not particularly well put. Downvoted – Aluan Haddad Oct 20 '16 at 23:05
  • 4
    `let` is not just developers desiring more complexity. It is actually intuitively more understandable, because when you loop-and-close over vars, the closure maintains the last value of the var, but when you do the same over lets, each closure in the loop has its own value for let, a la the example by @Jerry101 above – TKoL Dec 28 '16 at 13:33