ECMAScript 6 introduced the 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?
ECMAScript 6 introduced the 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?
Here's an explanation of the let keyword with some examples.
letworks very much likevar. The main difference is that the scope of avarvariable 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.
for (let i = 0; i < 5; i++) {
// i accessible ✔️
}
// i not accessible ❌
for (var i = 0; i < 5; i++) {
// i accessible ✔️
}
// i accessible ✔️
⚡️ Sandbox to play around ↓
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.
ES6 introduced two new keyword(let and const) alternate to var.
When you need a block level deceleration you can go with let and const instead of var.
The below table summarize the difference between var, let and const
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
letbe 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).
This explanation was taken from an article I wrote at Medium:
Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope by the parser which reads the source code into an intermediate representation before the actual code execution starts by the JavaScript interpreter. So, it actually doesn’t matter where variables or functions are declared, they will be moved to the top of their scope regardless of whether their scope is global or local. This means that
console.log (hi); var hi = "say hi";is actually interpreted to
var hi = undefined; console.log (hi); hi = "say hi";So, as we saw just now,
varvariables are being hoisted to the top of their scope and are being initialized with the value of undefined which means that we can actually assign their value before actually declaring them in the code like so:hi = “say hi” console.log (hi); // say hi var hi;Regarding function declarations, we can invoke them before actually declaring them like so:
sayHi(); // Hi function sayHi() { console.log('Hi'); };Function expressions, on the other hand, are not hoisted, so we’ll get the following error:
sayHi(); //Output: "TypeError: sayHi is not a function var sayHi = function() { console.log('Hi'); };ES6 introduced JavaScript developers the
letandconstkeywords. Whileletandconstare block-scoped and not function scoped asvarit shouldn’t make a difference while discussing their hoisting behavior. We’ll start from the end, JavaScript hoistsletandconst.console.log(hi); // Output: Cannot access 'hi' before initialization let hi = 'Hi';As we can see above,
letdoesn’t allow us to use undeclared variables, hence the interpreter explicitly output a reference error indicating that thehivariable cannot be accessed before initialization. The same error will occur if we change the abovelettoconstconsole.log(hi); // Output: Cannot access 'hi' before initialization const hi = 'Hi';So, bottom line, the JavaScript parser searches for variable declarations and functions and hoists them to the top of their scope before code execution and assign values to them in the memory so in case the interpreter will encounter them while executing the code he will recognize them and will be able to execute the code with their assigned values. Variables declared with
letorconstremain uninitialized at the beginning of execution while that variables declared withvarare being initialized with a value ofundefined.I added this visual illustration to help understanding of how are the hoisted variables and function are being saved in the memory
var is global scope (hoist-able) variable.
let and const is block scope.
test.js
{
let l = 'let';
const c = 'const';
var v = 'var';
v2 = 'var 2';
}
console.log(v, this.v);
console.log(v2, this.v2);
console.log(l); // ReferenceError: l is not defined
console.log(c); // ReferenceError: c is not defined
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
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.
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.
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.
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.
As mentioned above:
The difference is scoping.
varis scoped to the nearest function block andletis 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.
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.
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 :(
}
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";
function doWork() { // msg will be available since it was defined above this opening bracket!
let friends = 0;
console.log(msg);
// with VAR though:
for (var iCount2 = 0; iCount2 < 5; iCount2++) {} // iCount2 will be available after this closing bracket!
console.log(iCount2);
for (let iCount1 = 0; iCount1 < 5; iCount1++) {} // iCount1 will not be available behind this closing bracket, it will return undefined
console.log(iCount1);
} // friends will no be available after this closing bracket!
doWork();
console.log(friends);
var --> Function scope
let --> Block scope
const --> Block scope
var
In this code sample, variable i is declared using var. Therefore, it has a function scope. It means you can access i from only inside the function x. You can't read it from outside the function x
function x(){
var i = 100;
console.log(i); // 100
}
console.log(i); // Error. You can't do this
x();
In this sample, you can see i is declared inside a if block. But it's declared using var. Therefore, it gets function scope. It means still you can access variable i inside function x. Because var always get scoped to functions. Even though variable i is declared inside if block, because of it's using var it get scoped to parent function x.
function x(){
if(true){
var i = 100;
}
console.log(i);
}
x();
Now variable i is declared inside the function y. Therefore, i scoped to function y. You can access i inside function y. But not from outside function y.
function x(){
function y(){
var i = 100;
console.log(i);
}
y();
}
x();
function x(){
function y(){
var i = 100;
}
console.log(i); // ERROR
}
x();
let, const
let and const has block scope.
const and let behave same. But the difference is, when you assign value to const you can't re-assign. But you can re-assign values with let.
In this example, variable i is declared inside an if block. So it can be only accessed from inside that if block. We can't access it from outside that if block. (here const work same as let)
if(true){
let i = 100;
console.log(i); // Output: 100
}
console.log(i); // Error
function x(){
if(true){
let i = 100;
console.log(i); // Output: 100
}
console.log(i); // Error
}
x();
Another difference with (let, const) vs var is you can access var defined variable before declaring it. It will give you undefined. But if you do that with let or const defined variable it will give you an error.
console.log(x);
var x = 100;
console.log(x); // ERROR
let x = 100;
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
const name = 'Max';
let age = 33;
var hasHobbies = true;
name = 'Maximilian';
age = 34;
hasHobbies = false;
const summarizeUser = (userName, userAge, userHasHobby) => {
return (
'Name is ' +
userName +
', age is ' +
userAge +
' and the user has hobbies: ' +
userHasHobby
);
}
console.log(summarizeUser(name, age, hasHobbies));
As you can see from running the code above, when you try to change the const variable, you will get an error:
Attempting to override 'name' which is a constant.
or
TypeError: Invalid assignment to const 'name'.
but take a look at the let variable.
First we declare let age = 33, and later assign some other value age = 34;, which is OK; we don't have any errors when we try to change let variable
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.
Var
Before the advent of ES6, var declarations ruled. There are issues associated with variables declared with var, though. That is why it was necessary for new ways to declare variables to emerge. First, let's get to understand var more before we discuss those issues.
Scope of var
Scope essentially means where these variables are available for use. var declarations are globally scoped or function/locally scoped.
The scope is global when a var variable is declared outside a function. This means that any variable that is declared with var outside a function block is available for use in the whole window.
var is function scoped when it is declared within a function. This means that it is available and can be accessed only within that function.
To understand further, look at the example below.
var greeter = "hey hi";
function newFunction() {
var hello = "hello";
}
Here, greeter is globally scoped because it exists outside a function while hello is function scoped. So we cannot access the variable hello outside of a function. So if we do this:
var tester = "hey hi";
function newFunction() {
var hello = "hello";
}
console.log(hello); // error: hello is not defined
We'll get an error which is as a result of hello not being available outside the function.
var variables can be re-declared and updated
This means that we can do this within the same scope and won't get an error.
var greeter = "hey hi";
var greeter = "say Hello instead";
and this also
var greeter = "hey hi";
greeter = "say Hello instead";
Hoisting of var
Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope before code execution. This means that if we do this:
console.log (greeter);
var greeter = "say hello"
it is interpreted as this:
var greeter;
console.log(greeter); // greeter is undefined
greeter = "say hello"
So var variables are hoisted to the top of their scope and initialized with a value of undefined.
Problem with var
There's a weakness that comes with var. I'll use the example below to explain:
var greeter = "hey hi";
var times = 4;
if (times > 3) {
var greeter = "say Hello instead";
}
console.log(greeter) // "say Hello instead"
So, since times > 3 returns true, greeter is redefined to "say Hello instead". While this is not a problem if you knowingly want greeter to be redefined, it becomes a problem when you do not realize that a variable greeter has already been defined before.
If you have used greeter in other parts of your code, you might be surprised at the output you might get. This will likely cause a lot of bugs in your code. This is why let and const are necessary.
Let
let is now preferred for variable declaration. It's no surprise as it comes as an improvement to var declarations. It also solves the problem with var that we just covered. Let's consider why this is so.
let is block scoped
A block is a chunk of code bounded by {}. A block lives in curly braces. Anything within curly braces is a block.
So a variable declared in a block with let is only available for use within that block. Let me explain this with an example:
let greeting = "say Hi";
let times = 4;
if (times > 3) {
let hello = "say Hello instead";
console.log(hello);// "say Hello instead"
}
console.log(hello) // hello is not defined
We see that using hello outside its block (the curly braces where it was defined) returns an error. This is because let variables are block scoped .
let can be updated but not re-declared.
Just like var, a variable declared with let can be updated within its scope. Unlike var, a let variable cannot be re-declared within its scope. So while this will work:
let greeting = "say Hi";
greeting = "say Hello instead";
this will return an error:
let greeting = "say Hi";
let greeting = "say Hello instead"; // error: Identifier 'greeting' has already been declared
However, if the same variable is defined in different scopes, there will be no error:
let greeting = "say Hi";
if (true) {
let greeting = "say Hello instead";
console.log(greeting); // "say Hello instead"
}
console.log(greeting); // "say Hi"
Why is there no error? This is because both instances are treated as different variables since they have different scopes.
This fact makes let a better choice than var. When using let, you don't have to bother if you have used a name for a variable before as a variable exists only within its scope.
Also, since a variable cannot be declared more than once within a scope, then the problem discussed earlier that occurs with var does not happen.
Hoisting of let
Just like var, let declarations are hoisted to the top. Unlike var which is initialized as undefined, the let keyword is not initialized. So if you try to use a let variable before declaration, you'll get a Reference Error.