let
statement.I've heard that it's described as a local
variable, but I'm still not quite sure how it behaves differently than the var
keyword.
What are the differences?. When should let
be used instead of var
?
This question is related to
javascript
scope
ecmascript-6
var
let
Before 2015, using the var
keyword was the only way to declare a JavaScript variable.
After ES6 (a JavaScript version), it allows 2 new keywords let & const.
let
= can be re-assigned
const
= cannot be re-assigned ( const which comes from constant, short-form 'const' )
Example:
Suppose, declaring a Country Name / Your mother name, const
is most suitable here. because there is less chance to change a country name or your mother name soon or later.
Your age, weight, salary, speed of a bike and more like these types of data that change often or need to reassign. those situations, let
is used.
It also appears that, at least in Visual Studio 2015, TypeScript 1.5, "var" allows multiple declarations of the same variable name in a block, and "let" doesn't.
This won't generate a compile error:
var x = 1;
var x = 2;
This will:
let x = 1;
let x = 2;
The difference is in the scope of the variables declared with each.
In practice, there are a number of useful consequences of the difference in scope:
let
variables are only visible in their nearest enclosing block ({ ... }
).let
variables are only usable in lines of code that occur after the variable is declared (even though they are hoisted!).let
variables may not be redeclared by a subsequent var
or let
.let
variables are not added to the global window
object.let
variables are easy to use with closures (they do not cause race conditions).The restrictions imposed by let
reduce the visibility of the variables and increase the likelihood that unexpected name collisions will be found early. This makes it easier to track and reason about variables, including their reachability(helping with reclaiming unused memory).
Consequently, let
variables are less likely to cause problems when used in large programs or when independently-developed frameworks are combined in new and unexpected ways.
var
may still be useful if you are sure you want the single-binding effect when using a closure in a loop (#5) or for declaring externally-visible global variables in your code (#4). Use of var
for exports may be supplanted if export
migrates out of transpiler space and into the core language.
1. No use outside nearest enclosing block:
This block of code will throw a reference error because the second use of x
occurs outside of the block where it is declared with let
:
{
let x = 1;
}
console.log(`x is ${x}`); // ReferenceError during parsing: "x is not defined".
In contrast, the same example with var
works.
2. No use before declaration:
This block of code will throw a ReferenceError
before the code can be run because x
is used before it is declared:
{
x = x + 1; // ReferenceError during parsing: "x is not defined".
let x;
console.log(`x is ${x}`); // Never runs.
}
In contrast, the same example with var
parses and runs without throwing any exceptions.
3. No redeclaration:
The following code demonstrates that a variable declared with let
may not be redeclared later:
let x = 1;
let x = 2; // SyntaxError: Identifier 'x' has already been declared
4. Globals not attached to window
:
var button = "I cause accidents because my name is too common.";
let link = "Though my name is common, I am harder to access from other JS files.";
console.log(link); // OK
console.log(window.link); // undefined (GOOD!)
console.log(window.button); // OK
5. Easy use with closures:
Variables declared with var
do not work well with closures inside loops. Here is a simple loop that outputs the sequence of values that the variable i
has at different points in time:
for (let i = 0; i < 5; i++) {
console.log(`i is ${i}`), 125/*ms*/);
}
Specifically, this outputs:
i is 0
i is 1
i is 2
i is 3
i is 4
In JavaScript we often use variables at a significantly later time than when they are created. When we demonstrate this by delaying the output with a closure passed to setTimeout
:
for (let i = 0; i < 5; i++) {
setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}
... the output remains unchanged as long as we stick with let
. In contrast, if we had used var i
instead:
for (var i = 0; i < 5; i++) {
setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}
... the loop unexpectedly outputs "i is 5" five times:
i is 5
i is 5
i is 5
i is 5
i is 5
If I read the specs right then let
thankfully can also be leveraged to avoid self invoking functions used to simulate private only members - a popular design pattern that decreases code readability, complicates debugging, that adds no real code protection or other benefit - except maybe satisfying someone's desire for semantics, so stop using it. /rant
var SomeConstructor;
{
let privateScope = {};
SomeConstructor = function SomeConstructor () {
this.someProperty = "foo";
privateScope.hiddenProperty = "bar";
}
SomeConstructor.prototype.showPublic = function () {
console.log(this.someProperty); // foo
}
SomeConstructor.prototype.showPrivate = function () {
console.log(privateScope.hiddenProperty); // bar
}
}
var myInstance = new SomeConstructor();
myInstance.showPublic();
myInstance.showPrivate();
console.log(privateScope.hiddenProperty); // error
The accepted answer is missing a point:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
May the following two functions show the difference:
function varTest() {
var x = 31;
if (true) {
var x = 71; // Same variable!
console.log(x); // 71
}
console.log(x); // 71
}
function letTest() {
let x = 31;
if (true) {
let x = 71; // Different variable
console.log(x); // 71
}
console.log(x); // 31
}
As mentioned above:
The difference is scoping.
var
is scoped to the nearest function block andlet
is scoped to the nearest enclosing block, which can be smaller than a function block. Both are global if outside any block.Lets see an example:
Example1:
In my both examples I have a function myfunc
. myfunc
contains a variable myvar
equals to 10.
In my first example I check if myvar
equals to 10 (myvar==10
) . If yes, I agian declare a variable myvar
(now I have two myvar variables)using var
keyword and assign it a new value (20). In next line I print its value on my console. After the conditional block I again print the value of myvar
on my console. If you look at the output of myfunc
, myvar
has value equals to 20.
Example2:
In my second example instead of using var
keyword in my conditional block I declare myvar
using let
keyword . Now when I call myfunc
I get two different outputs: myvar=20
and myvar=10
.
So the difference is very simple i.e its scope.
When Using let
The let
keyword attaches the variable declaration to the scope of whatever block (commonly a { .. }
pair) it's contained in. In other words,let
implicitly hijacks any block's scope for its variable declaration.
let
variables cannot be accessed in the window
object because they cannot be globally accessed.
function a(){
{ // this is the Max Scope for let variable
let x = 12;
}
console.log(x);
}
a(); // Uncaught ReferenceError: x is not defined
When Using var
var
and variables in ES5 has scopes in functions meaning the variables are valid within the function and not outside the function itself.
var
variables can be accessed in the window
object because they cannot be globally accessed.
function a(){ // this is the Max Scope for var variable
{
var x = 12;
}
console.log(x);
}
a(); // 12
If you want to know more continue reading below
one of the most famous interview questions on scope also can suffice the exact use of let
and var
as below;
When using let
for (let i = 0; i < 10 ; i++) {
setTimeout(
function a() {
console.log(i); //print 0 to 9, that is literally AWW!!!
},
100 * i);
}
This is because when using let
, for every loop iteration the variable is scoped and has its own copy.
When using var
for (var i = 0; i < 10 ; i++) {
setTimeout(
function a() {
console.log(i); //print 10 times 10
},
100 * i);
}
This is because when using var
, for every loop iteration the variable is scoped and has shared copy.
Take a look at this image, I created one very simple example for demonstration of const
and let
variables. As you can see, when you try to change const
variable, you will get the error (Attempting to override 'name' which is constant'), but take a look at let
variable...
First we declare let age = 33
, and later assign some other value age = 34;
, which is ok, we dont have any errors when we try to change let
variable
var
is global scope (hoist-able) variable.
let
and const
is block scope.
test.js
{_x000D_
let l = 'let';_x000D_
const c = 'const';_x000D_
var v = 'var';_x000D_
v2 = 'var 2';_x000D_
}_x000D_
_x000D_
console.log(v, this.v);_x000D_
console.log(v2, this.v2);_x000D_
console.log(l); // ReferenceError: l is not defined_x000D_
console.log(c); // ReferenceError: c is not defined
_x000D_
The main difference is the scope difference, while let can be only available inside the scope it's declared, like in for loop, var can be accessed outside the loop for example. From the documentation in MDN (examples also from MDN):
let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
Variables declared by let have as their scope the block in which they are defined, as well as in any contained sub-blocks. In this way, let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function:
function varTest() {
var x = 1;
if (true) {
var x = 2; // same variable!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
if (true) {
let x = 2; // different variable
console.log(x); // 2
}
console.log(x); // 1
}`
At the top level of programs and functions, let, unlike var, does not create a property on the global object. For example:
var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
When used inside a block, let limits the variable's scope to that block. Note the difference between var whose scope is inside the function where it is declared.
var a = 1;
var b = 2;
if (a === 1) {
var a = 11; // the scope is global
let b = 22; // the scope is inside the if-block
console.log(a); // 11
console.log(b); // 22
}
console.log(a); // 11
console.log(b); // 2
Also don't forget it's ECMA6 feature, so it's not fully supported yet, so it's better always transpiles it to ECMA5 using Babel etc... for more info about visit babel website
This article clearly defines the difference between var, let and const
const
is a signal that the identifier won’t be reassigned.
let
, is a signal that the variable may be reassigned, such as a counter in a loop, or a value swap in an algorithm. It also signals that the variable will be used only in the block it’s defined in, which is not always the entire containing function.
var
is now the weakest signal available when you define a variable in JavaScript. The variable may or may not be reassigned, and the variable may or may not be used for an entire function, or just for the purpose of a block or loop.
https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b
let
is interesting, because it allows us to do something like this:
(() => {
var count = 0;
for (let i = 0; i < 2; ++i) {
for (let i = 0; i < 2; ++i) {
for (let i = 0; i < 2; ++i) {
console.log(count++);
}
}
}
})();
Which results in counting [0, 7].
Whereas
(() => {
var count = 0;
for (var i = 0; i < 2; ++i) {
for (var i = 0; i < 2; ++i) {
for (var i = 0; i < 2; ++i) {
console.log(count++);
}
}
}
})();
Only counts [0, 1].
I just came across one use case that I had to use var
over let
to introduce new variable. Here's a case:
I want to create a new variable with dynamic variable names.
let variableName = 'a';
eval("let " + variableName + '= 10;');
console.log(a); // this doesn't work
var variableName = 'a';
eval("var " + variableName + '= 10;');
console.log(a); // this works
The above code doesn't work because eval
introduces a new block of code. The declaration using var
will declare a variable outside of this block of code since var
declares a variable in the function scope.
let
, on the other hand, declares a variable in a block scope. So, a
variable will only be visible in eval
block.
There are some subtle differences — let
scoping behaves more like variable scoping does in more or less any other languages.
e.g. It scopes to the enclosing block, They don't exist before they're declared, etc.
However it's worth noting that let
is only a part of newer Javascript implementations and has varying degrees of browser support.
let
and var
?var
statement is known throughout the function it is defined in, from the start of the function. (*)let
statement is only known in the block it is defined in, from the moment it is defined onward. (**)To understand the difference, consider the following code:
// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here
function loop(arr) {
// i IS known here, but undefined
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( var i = 0; i < arr.length; i++ ) {
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( let j = 0; j < arr.length; j++ ) {
// i IS known here, and has a value
// j IS known here, and has a value
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
}
loop([1,2,3,4]);
for( var k = 0; k < arr.length; k++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
};
for( let l = 0; l < arr.length; l++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS known here, and has a value
};
loop([1,2,3,4]);
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
Here, we can see that our variable j
is only known in the first for loop, but not before and after. Yet, our variable i
is known in the entire function.
Also, consider that block scoped variables are not known before they are declared because they are not hoisted. You're also not allowed to redeclare the same block scoped variable within the same block. This makes block scoped variables less error prone than globally or functionally scoped variables, which are hoisted and which do not produce any errors in case of multiple declarations.
let
today?Some people would argue that in the future we'll ONLY use let statements and that var statements will become obsolete. JavaScript guru Kyle Simpson wrote a very elaborate article on why he believes that won't be the case.
Today, however, that is definitely not the case. In fact, we need actually to ask ourselves whether it's safe to use the let
statement. The answer to that question depends on your environment:
If you're writing server-side JavaScript code (Node.js), you can safely use the let
statement.
If you're writing client-side JavaScript code and use a browser based transpiler (like Traceur or babel-standalone), you can safely use the let
statement, however your code is likely to be anything but optimal with respect to performance.
If you're writing client-side JavaScript code and use a Node based transpiler (like the traceur shell script or Babel), you can safely use the let
statement. And because your browser will only know about the transpiled code, performance drawbacks should be limited.
If you're writing client-side JavaScript code and don't use a transpiler, you need to consider browser support.
There are still some browsers that don't support let
at all :
For an up-to-date overview of which browsers support the let
statement at the time of your reading this answer, see this Can I Use
page.
(*) Globally and functionally scoped variables can be initialized and used before they are declared because JavaScript variables are hoisted. This means that declarations are always much to the top of the scope.
(**) Block scoped variables are not hoisted
Here's an explanation of the let
keyword with some examples.
let
works very much likevar
. The main difference is that the scope of avar
variable is the entire enclosing function
This table on Wikipedia shows which browsers support Javascript 1.7.
Note that only Mozilla and Chrome browsers support it. IE, Safari, and potentially others don't.
I want to link these keywords to the Execution Context, because the Execution Context is important in all of this. The Execution Context has two phases: a Creation Phase and Execution Phase. In addition, each Execution Context has a Variable Environment and Outer Environment (its Lexical Environment).
During the Creation Phase of an Execution Context, var, let and const will still store its variable in memory with an undefined value in the Variable Environment of the given Execution Context. The difference is in the Execution Phase. If you use reference a variable defined with var before it is assigned a value, it will just be undefined. No exception will be raised.
However, you cannot reference the variable declared with let or const until it is declared. If you try to use it before it is declared, then an exception will be raised during the Execution Phase of the Execution Context. Now the variable will still be in memory, courtesy of the Creation Phase of the Execution Context, but the Engine will not allow you to use it:
function a(){
b;
let b;
}
a();
> Uncaught ReferenceError: b is not defined
With a variable defined with var, if the Engine cannot find the variable in the current Execution Context's Variable Environment, then it will go up the scope chain (the Outer Environment) and check the Outer Environment's Variable Environment for the variable. If it cannot find it there, it will continue searching the Scope Chain. This is not the case with let and const.
The second feature of let is it introduces block scope. Blocks are defined by curly braces. Examples include function blocks, if blocks, for blocks, etc. When you declare a variable with let inside of a block, the variable is only available inside of the block. In fact, each time the block is run, such as within a for loop, it will create a new variable in memory.
ES6 also introduces the const keyword for declaring variables. const is also block scoped. The difference between let and const is that const variables need to be declared using an initializer, or it will generate an error.
And, finally, when it comes to the Execution Context, variables defined with var will be attached to the 'this' object. In the global Execution Context, that will be the window object in browsers. This is not the case for let or const.
I think the terms and most of the examples are a bit overwhelming,
The main issue i had personally with the difference is understanding what a "Block" is.
At some point i realized, a block would be any curly brackets except for IF
statement.
an opening bracket {
of a function or loop will define a new block, anything defined with let
within it, will not be available after the closing bracket }
of the same thing (function or loop);
With that in mind, it was easier to understand:
let msg = "Hello World";_x000D_
_x000D_
function doWork() { // msg will be available since it was defined above this opening bracket!_x000D_
let friends = 0;_x000D_
console.log(msg);_x000D_
_x000D_
// with VAR though:_x000D_
for (var iCount2 = 0; iCount2 < 5; iCount2++) {} // iCount2 will be available after this closing bracket!_x000D_
console.log(iCount2);_x000D_
_x000D_
for (let iCount1 = 0; iCount1 < 5; iCount1++) {} // iCount1 will not be available behind this closing bracket, it will return undefined_x000D_
console.log(iCount1);_x000D_
_x000D_
} // friends will no be available after this closing bracket!_x000D_
doWork();_x000D_
console.log(friends);
_x000D_
ECMAScript 6 added one more keyword to declare variables other the "const" other than "let".
The primary goal of introduction of "let" and "const" over "var" is to have block scoping instead of traditional lexical scoping. This article explains very briefly difference between "var" and "let" and it also covers the discussion on "const".
Here is an example for the difference between the two (support just started for chrome):
As you can see the var j
variable is still having a value outside of the for loop scope (Block Scope), but the let i
variable is undefined outside of the for loop scope.
"use strict";_x000D_
console.log("var:");_x000D_
for (var j = 0; j < 2; j++) {_x000D_
console.log(j);_x000D_
}_x000D_
_x000D_
console.log(j);_x000D_
_x000D_
console.log("let:");_x000D_
for (let i = 0; i < 2; i++) {_x000D_
console.log(i);_x000D_
}_x000D_
_x000D_
console.log(i);
_x000D_
Check this link in MDN
let x = 1;
if (x === 1) {
let x = 2;
console.log(x);
// expected output: 2
}
console.log(x);
// expected output: 1
let
can also be used to avoid problems with closures. It binds fresh value rather than keeping an old reference as shown in examples below.
for(var i=1; i<6; i++) {_x000D_
$("#div" + i).click(function () { console.log(i); });_x000D_
}
_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>_x000D_
<p>Clicking on each number will log to console:</p> _x000D_
<div id="div1">1</div>_x000D_
<div id="div2">2</div>_x000D_
<div id="div3">3</div>_x000D_
<div id="div4">4</div>_x000D_
<div id="div5">5</div>
_x000D_
Code above demonstrates a classic JavaScript closure problem. Reference to the i
variable is being stored in the click handler closure, rather than the actual value of i
.
Every single click handler will refer to the same object because there’s only one counter object which holds 6 so you get six on each click.
A general workaround is to wrap this in an anonymous function and pass i
as an argument. Such issues can also be avoided now by using let
instead var
as shown in the code below.
(Tested in Chrome and Firefox 50)
for(let i=1; i<6; i++) {_x000D_
$("#div" + i).click(function () { console.log(i); });_x000D_
}
_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>_x000D_
<p>Clicking on each number will log to console:</p> _x000D_
<div id="div1">1</div>_x000D_
<div id="div2">2</div>_x000D_
<div id="div3">3</div>_x000D_
<div id="div4">4</div>_x000D_
<div id="div5">5</div>
_x000D_
Now I think there is better scoping of variables to a block of statements using let
:
function printnums()
{
// i is not accessible here
for(let i = 0; i <10; i+=)
{
console.log(i);
}
// i is not accessible here
// j is accessible here
for(var j = 0; j <10; j++)
{
console.log(j);
}
// j is accessible here
}
I think people will start using let here after so that they will have similar scoping in JavaScript like other languages, Java, C#, etc.
People with not a clear understanding about scoping in JavaScript used to make the mistake earlier.
Hoisting is not supported using let
.
With this approach errors present in JavaScript are getting removed.
Refer to ES6 In Depth: let and const to understand it better.
The main difference between var
and let
is that variables declared with var
are function scoped. Whereas functions declared with let
are block scoped. For example:
function testVar () {
if(true) {
var foo = 'foo';
}
console.log(foo);
}
testVar();
// logs 'foo'
function testLet () {
if(true) {
let bar = 'bar';
}
console.log(bar);
}
testLet();
// reference error
// bar is scoped to the block of the if statement
variables with var
:
When the first function testVar
gets called the variable foo, declared with var
, is still accessible outside the if
statement. This variable foo
would be available everywhere within the scope of the testVar
function.
variables with let
:
When the second function testLet
gets called the variable bar, declared with let
, is only accessible inside the if
statement. Because variables declared with let
are block scoped (where a block is the code between curly brackets e.g if{}
, for{}
, function{}
).
let
variables don't get hoisted:Another difference between var
and let
is variables with declared with let
don't get hoisted. An example is the best way to illustrate this behavior:
variables with let
don't get hoisted:
console.log(letVar);
let letVar = 10;
// referenceError, the variable doesn't get hoisted
variables with var
do get hoisted:
console.log(varVar);
var varVar = 10;
// logs undefined, the variable gets hoisted
let
doesn't get attached to window
:A variable declared with let
in the global scope (which is code that is not in a function) doesn't get added as a property on the global window
object. For example (this code is in global scope):
var bar = 5;
let foo = 10;
console.log(bar); // logs 5
console.log(foo); // logs 10
console.log(window.bar);
// logs 5, variable added to window object
console.log(window.foo);
// logs undefined, variable not added to window object
When should
let
be used overvar
?
Use let
over var
whenever you can because it is simply scoped more specific. This reduces potential naming conflicts which can occur when dealing with a large number of variables. var
can be used when you want a global variable explicitly to be on the window
object (always consider carefully if this is really necessary).
let is a part of es6. These functions will explain the difference in easy way.
function varTest() {
var x = 1;
if (true) {
var x = 2; // same variable!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
if (true) {
let x = 2; // different variable
console.log(x); // 2
}
console.log(x); // 1
}
Here's an example to add on to what others have already written. Suppose you want to make an array of functions, adderFunctions
, where each function takes a single Number argument and returns the sum of the argument and the function's index in the array. Trying to generate adderFunctions
with a loop using the var
keyword won't work the way someone might naïvely expect:
// An array of adder functions.
var adderFunctions = [];
for (var i = 0; i < 1000; i++) {
// We want the function at index i to add the index to its argument.
adderFunctions[i] = function(x) {
// What is i bound to here?
return x + i;
};
}
var add12 = adderFunctions[12];
// Uh oh. The function is bound to i in the outer scope, which is currently 1000.
console.log(add12(8) === 20); // => false
console.log(add12(8) === 1008); // => true
console.log(i); // => 1000
// It gets worse.
i = -8;
console.log(add12(8) === 0); // => true
The process above doesn't generate the desired array of functions because i
's scope extends beyond the iteration of the for
block in which each function was created. Instead, at the end of the loop, the i
in each function's closure refers to i
's value at the end of the loop (1000) for every anonymous function in adderFunctions
. This isn't what we wanted at all: we now have an array of 1000 different functions in memory with exactly the same behavior. And if we subsequently update the value of i
, the mutation will affect all the adderFunctions
.
However, we can try again using the let
keyword:
// Let's try this again.
// NOTE: We're using another ES6 keyword, const, for values that won't
// be reassigned. const and let have similar scoping behavior.
const adderFunctions = [];
for (let i = 0; i < 1000; i++) {
// NOTE: We're using the newer arrow function syntax this time, but
// using the "function(x) { ..." syntax from the previous example
// here would not change the behavior shown.
adderFunctions[i] = x => x + i;
}
const add12 = adderFunctions[12];
// Yay! The behavior is as expected.
console.log(add12(8) === 20); // => true
// i's scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined
This time, i
is rebound on each iteration of the for
loop. Each function now keeps the value of i
at the time of the function's creation, and adderFunctions
behaves as expected.
Now, image mixing the two behaviors and you'll probably see why it's not recommended to mix the newer let
and const
with the older var
in the same script. Doing so can result is some spectacularly confusing code.
const doubleAdderFunctions = [];
for (var i = 0; i < 1000; i++) {
const j = i;
doubleAdderFunctions[i] = x => x + i + j;
}
const add18 = doubleAdderFunctions[9];
const add24 = doubleAdderFunctions[12];
// It's not fun debugging situations like this, especially when the
// code is more complex than in this example.
console.log(add18(24) === 42); // => false
console.log(add24(18) === 42); // => false
console.log(add18(24) === add24(18)); // => false
console.log(add18(24) === 2018); // => false
console.log(add24(18) === 2018); // => false
console.log(add18(24) === 1033); // => true
console.log(add24(18) === 1030); // => true
Don't let this happen to you. Use a linter.
NOTE: This is a teaching example intended to demonstrate the
var
/let
behavior in loops and with function closures that would also be easy to understand. This would be a terrible way to add numbers. But the general technique of capturing data in anonymous function closures might be encountered in the real world in other contexts. YMMV.
As I am currently trying to get an in depth understanding of JavaScript I will share my brief research which contains some of the great pieces already discussed plus some other details in a different perspective.
Understanding the difference between var and let can be easier if we understand the difference between function and block scope.
Let's consider the following cases:
(function timer() {
for(var i = 0; i <= 5; i++) {
setTimeout(function notime() { console.log(i); }, i * 1000);
}
})();
Stack VariableEnvironment //one VariablEnvironment for timer();
// when the timer is out - the value will be the same value for each call
5. [setTimeout, i] [i=5]
4. [setTimeout, i]
3. [setTimeout, i]
2. [setTimeout, i]
1. [setTimeout, i]
0. [setTimeout, i]
####################
(function timer() {
for (let i = 0; i <= 5; i++) {
setTimeout(function notime() { console.log(i); }, i * 1000);
}
})();
Stack LexicalEnvironment - each iteration has a new lexical environment
5. [setTimeout, i] [i=5]
LexicalEnvironment
4. [setTimeout, i] [i=4]
LexicalEnvironment
3. [setTimeout, i] [i=3]
LexicalEnvironment
2. [setTimeout, i] [i=2]
LexicalEnvironment
1. [setTimeout, i] [i=1]
LexicalEnvironment
0. [setTimeout, i] [i=0]
when timer()
gets called an ExecutionContext is created which will contain both the VariableEnvironment and all the LexicalEnvironments corresponding to each iteration.
And a simpler example
Function Scope
function test() {
for(var z = 0; z < 69; z++) {
//todo
}
//z is visible outside the loop
}
Block Scope
function test() {
for(let z = 0; z < 69; z++) {
//todo
}
//z is not defined :(
}
Variable Not Hoisting
let
will not hoist to the entire scope of the block they appear in. By contrast, var
could hoist as below.
{
console.log(cc); // undefined. Caused by hoisting
var cc = 23;
}
{
console.log(bb); // ReferenceError: bb is not defined
let bb = 23;
}
Actually, Per @Bergi, Both var
and let
are hoisted.
Garbage Collection
Block scope of let
is useful relates to closures and garbage collection to reclaim memory. Consider,
function process(data) {
//...
}
var hugeData = { .. };
process(hugeData);
var btn = document.getElementById("mybutton");
btn.addEventListener( "click", function click(evt){
//....
});
The click
handler callback does not need the hugeData
variable at all. Theoretically, after process(..)
runs, the huge data structure hugeData
could be garbage collected. However, it's possible that some JS engine will still have to keep this huge structure, since the click
function has a closure over the entire scope.
However, the block scope can make this huge data structure to garbage collected.
function process(data) {
//...
}
{ // anything declared inside this block can be garbage collected
let hugeData = { .. };
process(hugeData);
}
var btn = document.getElementById("mybutton");
btn.addEventListener( "click", function click(evt){
//....
});
let
loops
let
in the loop can re-binds it to each iteration of the loop, making sure to re-assign it the value from the end of the previous loop iteration. Consider,
// print '5' 5 times
for (var i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i);
}, 1000);
}
However, replace var
with let
// print 1, 2, 3, 4, 5. now
for (let i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i);
}, 1000);
}
Because let
create a new lexical environment with those names for a) the initialiser expression b) each iteration (previosly to evaluating the increment expression), more details are here.
let
Variables declared using the let
keyword are block-scoped, which means that they are available only in the block in which they were declared.
At the top level, variables declared using let
don't create properties on the global object.
var globalVariable = 42;
let blockScopedVariable = 43;
console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43
console.log(this.globalVariable); // 42
console.log(this.blockScopedVariable); // undefined
Inside a function (but outside of a block), let
has the same scope as var
.
(() => {
var functionScopedVariable = 42;
let blockScopedVariable = 43;
console.log(functionScopedVariable); // 42
console.log(blockScopedVariable); // 43
})();
console.log(functionScopedVariable); // ReferenceError: functionScopedVariable is not defined
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined
Variables declared using let
inside a block can't be accessed outside that block.
{
var globalVariable = 42;
let blockScopedVariable = 43;
console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43
}
console.log(globalVariable); // 42
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined
Variables declared with let
in loops can be referenced only inside that loop.
for (var i = 0; i < 3; i++) {
var j = i * 2;
}
console.log(i); // 3
console.log(j); // 4
for (let k = 0; k < 3; k++) {
let l = k * 2;
}
console.log(typeof k); // undefined
console.log(typeof l); // undefined
// Trying to do console.log(k) or console.log(l) here would throw a ReferenceError.
If you use let
instead of var
in a loop, with each iteration you get a new variable. That means that you can safely use a closure inside a loop.
// Logs 3 thrice, not what we meant.
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
// Logs 0, 1 and 2, as expected.
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 0);
}
Because of the temporal dead zone, variables declared using let
can't be accessed before they are declared. Attempting to do so throws an error.
console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;
You can't declare the same variable multiple times using let
. You also can't declare a variable using let
with the same identifier as another variable which was declared using var
.
var a;
var a; // Works fine.
let b;
let b; // SyntaxError: Identifier 'b' has already been declared
var c;
let c; // SyntaxError: Identifier 'c' has already been declared
const
const
is quite similar to let
—it's block-scoped and has TDZ. There are, however, two things which are different.
Variable declared using const
can't be re-assigned.
const a = 42;
a = 43; // TypeError: Assignment to constant variable.
Note that it doesn't mean that the value is immutable. Its properties still can be changed.
const obj = {};
obj.a = 42;
console.log(obj.a); // 42
If you want to have an immutable object, you should use Object.freeze()
.
You always must specify a value when declaring a variable using const
.
const a; // SyntaxError: Missing initializer in const declaration
let vs var. It's all about scope.
var variables are global and can be accessed basically everywhere, while let variables are not global and only exist until a closing parenthesis kills them.
See my example below, and note how the lion (let) variable acts differently in the two console.logs; it becomes out of scope in the 2nd console.log.
var cat = "cat";
let dog = "dog";
var animals = () => {
var giraffe = "giraffe";
let lion = "lion";
console.log(cat); //will print 'cat'.
console.log(dog); //will print 'dog', because dog was declared outside this function (like var cat).
console.log(giraffe); //will print 'giraffe'.
console.log(lion); //will print 'lion', as lion is within scope.
}
console.log(giraffe); //will print 'giraffe', as giraffe is a global variable (var).
console.log(lion); //will print UNDEFINED, as lion is a 'let' variable and is now out of scope.
The below shows how 'let' and 'var' are different in the scope:
let gfoo = 123;
if (true) {
let gfoo = 456;
}
console.log(gfoo); // 123
var hfoo = 123;
if (true) {
var hfoo = 456;
}
console.log(hfoo); // 456
The gfoo
, defined by let
initially is in the global scope, and when we declare gfoo
again inside the if clause
its scope changed and when a new value is assigned to the variable inside that scope it does not affect the global scope.
Whereas hfoo
, defined by var
is initially in the global scope, but again when we declare it inside the if clause
, it considers the global scope hfoo, although var has been used again to declare it. And when we re-assign its value we see that the global scope hfoo is also affected. This is the primary difference.
Some hacks with let
:
1.
let statistics = [16, 170, 10];
let [age, height, grade] = statistics;
console.log(height)
2.
let x = 120,
y = 12;
[x, y] = [y, x];
console.log(`x: ${x} y: ${y}`);
3.
let node = {
type: "Identifier",
name: "foo"
};
let { type, name, value } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
console.log(value); // undefined
let node = {
type: "Identifier"
};
let { type: localType, name: localName = "bar" } = node;
console.log(localType); // "Identifier"
console.log(localName); // "bar"
let
:let jar = {
numberOfCookies: 10,
get cookies() {
return this.numberOfCookies;
},
set cookies(value) {
this.numberOfCookies = value;
}
};
console.log(jar.cookies)
jar.cookies = 7;
console.log(jar.cookies)
Source: Stackoverflow.com