Scope in JavaScript behaviour - javascript

This is a simple script that'll log the content of text from a Photoshop file.
var numOfLayers = app.activeDocument.layers.length;
var resultStr = "";
doAllLayers();
alert(resultStr);
function addToString(alayer)
{
if (alayer.kind == "LayerKind.TEXT")
{
var c = alayer.textItem.contents;
resultStr += c;
}
}
// main loop
function doAllLayers()
{
for (var i = numOfLayers -1; i >= 0; i--)
{
var thisLayer = app.activeDocument.layers[i];
addToString(thisLayer);
}
}
It works fine but I really should be passing a string into the function to add to itself, however it works.
How does the scope of JavaScript allow this to happen? Are declared local variables still accessed by functions or is it another trick I'm missing?

Here are some basic rules to variable scoping in JavaScript:
If defined with the var keyword, the variable is function-scoped. That is, the variable will be scoped to either the closest containing function, or to the global context if there is no containing function.
Example:
// globally-scoped variable (no containing function)
var foo = 'bar';
function test() {
// function-scoped variable
var foo2 = 'bar2';
if (true) {
// still function-scoped variable, regardless of the if-block
var foo3 = 'bar3';
}
// see?
console.log(foo3); // --> 'bar3'
}
If defined with the let keyword (ES6+), then the variable is block-scoped (this behavior more closely resembles most other C-family syntax languages). Example:
// error: top level "let" declarations aren't allowed
let foo = 'bar';
function test() {
// block-scoped variable (function are blocks, too)
let foo2 = 'bar2';
if (true) {
// block-scoped variable (notice the difference
// from the previous example?)
let foo3 = 'bar3';
}
// uh oh?
console.log(foo3); // --> ReferenceError: foo3 is not defined
}
If defined with neither the var or let keywords (e.g., foo = bar), then the variable is scoped to the global context. Example:
// globally-scoped variable
foo = 'bar';
function test() {
// also globally-scoped variable (no var or let declaration)
foo2 = 'bar2';
if (true) {
// still a globally-scoped variable
foo3 = 'bar3';
}
}
test();
console.log(foo, foo2, foo3); // 'bar', 'bar2', 'bar3'
In all of these cases, functions defined within scope of a variable still have access to the variable itself (technically you're creating a closure, as the numOfLayers and resultStr variables are lexically in scope of your addToString and doAllLayers functions).
Please note that the scoping rules are technically a little more nuanced than this, but you're better off reading a more in-depth article at that point.

You have defined var resultStr = ""; outside function which is a global variable. you can access this global variable inside addToString() and start concatenating to it. Note that inside the method you have not declared var resultStr = ""; else it would have been a different variable local to that method.

Related

JS new Function - logging a variable by name dynamically

We are given a code snippet in run time along with a name of a variable used in code. We want to evaluate the given code and log the value of the variable.
Example:
suppose our code is var foo = "Blah"; and the name of the var is foo.
eval('var foo = "Blah"; let varName = "foo"; console.log(this[varName]);');
yields Blah.
yet
new Function('var foo = "Blah"; let varName = "foo"; console.log(this[varName]);')();
yields undefined.
Is there a way to make get this to work with new Function?
With new Function, you're creating a new function. The code that is executed looks something like:
function someFunction() {
var foo = "Blah"; let varName = "foo"; console.log(this); console.log(this[varName]);
}
someFunction();
There, foo is in local scope of the someFunction. It's not global, so variables declared with var inside the function don't get put onto the global object.
In contrast, eval runs on the top level. Your eval code that is executed looks something like:
var foo = "Blah"; let varName = "foo"; console.log(this[varName]);
which is all on the top level, so the foo variable that was declared gets put onto the global object, the this.
Substitute the variable name into the function definition directly, rather than using this.
let varName = 'foo';
new Function(`var ${varName} = "Blah"; console.log(${varName});`)();
Everything here is about Scope. Global Scope and Block Scope.
Global Scope
Variables declared Globally (outside any function) have Global Scope.
var bikeName = "Honda";
// code here can use bikeName
function myFunction() {
// code here can also use bikeName
}
and
var greeter = "hey hi";
function newFunction() {
var hello = "hello"; //Variables declared Locally (inside a function) have Function Scope.
}
console.log(hello); // error: hello is not defined
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.
Block Scope
A block is a chunk of code bounded by {}. A block lives in curly braces.
let times = 4;
if (times > 3) {
let hello = "say Hello instead";
console.log(hello);// "say Hello instead"
}
console.log(hello) // hello is not defined
You should read about them. Things will get clear
Hence in your case:
new Function('var foo = "Blah"; let varName = "foo"; console.log(this[varName]);')();
Everything inside function has function scope and will not be available global!

Node.js ECMAScript 2015 - var vs let [duplicate]

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?
Scoping rules
The main difference is scoping rules. Variables declared by var keyword are scoped to the immediate function body (hence the function scope) while let variables are scoped to the immediate enclosing block denoted by { } (hence the block scope).
function run() {
var foo = "Foo";
let bar = "Bar";
console.log(foo, bar); // Foo Bar
{
var moo = "Mooo"
let baz = "Bazz";
console.log(moo, baz); // Mooo Bazz
}
console.log(moo); // Mooo
console.log(baz); // ReferenceError
}
run();
The reason why let keyword was introduced to the language was function scope is confusing and was one of the main sources of bugs in JavaScript.
Take a look at this example from another Stack Overflow question:
var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
// and store them in funcs
funcs[i] = function() {
// each should log its value.
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}
My value: 3 was output to console each time funcs[j](); was invoked since anonymous functions were bound to the same variable.
People had to create immediately invoked functions to capture correct values from the loops but that was also hairy.
Hoisting
While variables declared with var keyword are hoisted (initialized with undefined before the code is run) which means they are accessible in their enclosing scope even before they are declared:
function run() {
console.log(foo); // undefined
var foo = "Foo";
console.log(foo); // Foo
}
run();
let variables are not initialized until their definition is evaluated. Accessing them before the initialization results in a ReferenceError. The variable is said to be in "temporal dead zone" from the start of the block until the initialization is processed.
function checkHoisting() {
console.log(foo); // ReferenceError
let foo = "Foo";
console.log(foo); // Foo
}
checkHoisting();
Creating global object property
At the top level, let, unlike var, does not create a property on the global object:
var foo = "Foo"; // globally scoped
let bar = "Bar"; // not allowed to be globally scoped
console.log(window.foo); // Foo
console.log(window.bar); // undefined
Redeclaration
In strict mode, var will let you re-declare the same variable in the same scope while let raises a SyntaxError.
'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo1' is replaced with 'foo2'.
let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared
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++) {
$("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>
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++) {
$("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>
What's the difference between let and var?
A variable defined using a var statement is known throughout the function it is defined in, from the start of the function. (*)
A variable defined using a 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.
Is it safe to use 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 :
How to keep track of browser support
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 moved 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 like var. The main difference is that the scope of a var 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.
let
Block scope
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 (outside of a function)
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
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
Inside a block
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
Inside a loop
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.
Loops with closures
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);
}
Temporal dead zone
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;
No re-declaring
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.
No re-assigning
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().
const obj = Object.freeze({a: 40});
obj.a = 42;
console.log(obj.a); // 40
console.log(obj.b); // undefined
Initializer is required
You always must specify a value when declaring a variable using const.
const a; // SyntaxError: Missing initializer in const declaration
The accepted answer is missing a point:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
In most basic terms,
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 ↓
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
Here is an example of the difference between the two:
As you can see, the var j variable still has a value outside the for loop scope (Block Scope), but the let i variable is undefined outside of the for loop scope.
"use strict";
console.log("var:");
for (var j = 0; j < 2; j++) {
console.log(j);
}
console.log(j);
console.log("let:");
for (let i = 0; i < 2; i++) {
console.log(i);
}
console.log(i);
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.
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.
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.
Global 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.
Examples
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
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.
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
}
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
Function VS block scope:
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
Global 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 over var?
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 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].
This explanation is 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, var variables 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 let and const keywords. While let and const are block-scoped and not function
scoped as var it shouldn’t make a difference while discussing their
hoisting behavior. We’ll start from the end, JavaScript hoists let
and const.
console.log(hi); // Output: Cannot access 'hi' before initialization
let hi = 'Hi';
As we can see above, let doesn’t allow us to use undeclared
variables, hence the interpreter explicitly output a reference error
indicating that the hi variable cannot be accessed before
initialization. The same error will occur if we change the above let
to const
console.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 let or const remain
uninitialized at the beginning of execution while that variables
declared with var are being initialized with a value of undefined.
I added this visual illustration to better help understanding of how are the hoisted
variables and function are being saved in the memory
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;
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;
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
See 'Emulating private interfaces'
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.
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"
Getter and setter with 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)
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.
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.
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.
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
}
As mentioned above:
The difference is scoping. var is scoped to the nearest function
block and let 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.
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.

Javascript- Variable Hoisting

This is a simple snippet, I just dont understand something.
The below code outputs 12, I understand that, because the var foo = 12; replaces the previous declaration of the variable.
<script>
var foo = 1;
function bar(){
if (!foo) {
var foo = 12;
}
alert(foo);
}
bar();
</script>
In the below code, it alerts 1 , which means the variable declared outside the function is accessible inside the function.
<script>
var foo = 1;
function bar(){
alert(foo);
}
bar();
</script>
But, in the below code, why it alerts undefined ?? I thought, it will alert 1, I am just assigning the previously declared variable to the new one.
<script>
var foo = 1;
function bar(){
if (!foo) {
var foo = foo;
}
alert(foo);
}
bar();
</script>
Variable declarations are pushed to the start of the function.
Therefore in reality the following is happening:
function bar(){
var foo;
if (!foo) {
foo = foo;
}
alert(foo);
}
Therefore you would need to change this to use window.foo so that you're referring to the global property rather than the function's property:
var foo = 1;
function bar(){
var foo;
if (!window.foo) {
foo = window.foo;
}
alert(foo);
}
bar();
Hoisting is slightly tricky. Function declarations are hoisted with the function assignment, but variable declarations are hoisted without the variable assignment. So the execution order of code is actually:
var foo;
var bar = function bar(){
var foo; // undefined
if (!foo) { // true
foo = foo; // foo = undefined
}
alert(foo);
}
foo = 1;
bar();
You could either use window.foo if you want to refer to the global variable foo, or better, just use a different variable name:
var foo = 1;
function bar(){
var baz = foo;
alert(baz);
}
bar();
The below code outputs 12, I understand that, because the var foo =
12; replaces the previous declaration of the variable.
var foo = 1;
function bar(){
if (!foo) {
var foo = 12;
}
alert(foo);
}
bar();
You are right because local variable overriding the global one.
In the below code, it alerts 1 , which means the variable declared
outside the function is accessible inside the function.
var foo = 1;
function bar(){
alert(foo);
}
bar();
You are correct. foo is declare in global scope so is accessible fron anywhere.
But, in the below code, why it alerts undefined ?? I thought, it will
alert 1, I am just assigning the previously declared variable to the
new one.
var foo = 1;
function bar(){
if (!foo) {
var foo = foo;
}
alert(foo);
}
bar();
This is a bit different. You are declaring a global variable and a local one with the same name. When your JavaScript program execution enters a new function, all the variables declared anywhere in the function are moved (or elevated, or hoisted) to the top of the function.
Another example:
var a = 123;
function f() {
var a; // same as: var a = undefined;
alert(a); // undefined
a = 1;
alert(a); // 1
}
f();
In javascript, until the ES5 specification, the scope is implemented only in terms of function body. The concept of block scope doesn't exist (really, will be implemented in the next javascript with the let keyword).
So, if you declare a variable var something; outside from function body, it will be global (in browsers global scope is the scope of the window object).
global variables
var something = 'Hi Man';
/**
* this is equal to:
**/
window.something = 'Hi Man';
If your code doesn't run in strict mode, there is another way to declare a global variable: omitting the var keyword. When the var keyword is omitted the variable belongs (or is moved) to the global scope.
example:
something = 'Hi Man';
/**
* this is equal to:
**/
function someFunction() {
something = 'Hi Man';
}
Local Variables
Because the non-existence of block scopes the only way to declare a local variable is to define it in a function body.
Example
var something = 'Hi Man'; //global
console.log('globalVariable', something);
function someFunction() {
var something = 'Hi Woman';
console.log('localVariable', something);
/**
* defining variable that doesn't exists in global scope
**/
var localSomething = 'Hi People';
console.log('another local variable', localSomething);
}
someFunction();
console.log('globalVariable after function execution', something);
try {
console.log('try to access a local variable from global scope', localSomething);
} catch(e) { console.error(e); }
As you can see in this example, local variables don't exist outside from their scope. This means another thing... If you declare, with the var keyword, the same variable in two different scopes you'll get two different variables not an override of the same variable (name) defined in the parent scope.
If you want to "override" the same variable in a child scope you have to use it without the var keyword. Because of the scope chain if a variable dosn't exist in a local scope it will be searched on their parent scope.
Example
function someFunction() {
something = 'Hi Woman';
}
var something = 'Hi Man';
console.log(1, 'something is', something);
someFunction();
console.log(1, 'something is', something);
Last thing, variable hoistment.
As I wrote below, at the moment, there isn't any way to declare a variable in some point of your code. It is always declared at the start of it scope.
Example
function someFunction() {
// doing something
// doing something else
var something = 'Hi Man';
}
/**
* Probably you expect that the something variable will be defined after the 'doing
* something else' task, but, as javascript works, it will be defined on top of it scope.
* So, the below snippet is equal to:
**/
function someFunction1() {
var something;
// doing something
// doing something else
something = 'Hi Man';
}
/**
* You can try these following examples:
*
* In the someFunction2 we try to access on a non-defined variable and this throws an
* error.
*
* In the someFunction3, instead, we don't get any error because the variable that we expect to define later will be hoisted and defined at the top, so, the log is a simple undefined log.
**/
function someFunction2() {
console.log(something);
};
function someFunction3() {
console.log('before declaration', something);
var something = 'Hi Man';
console.log('after declaration', something);
}
This happens because in javascript there are two different steps of a variable declaration:
Definition
Initialization
And the function3 example becomes as following:
function3Explained() {
var something; // define it as undefined, this is the same as doing var something = undefined;
// doing something;
// doing something else;
something = 'Hi Man';
}
IMHO it doesn't have anything to do with function declaration and hoisting ,
declaring the var with var inside function you are creating a variable in the function's isolated scope, this is why you get undefined.
var foo = 1;
function funcOne() {
var foo = foo;
alert('foo is ' + foo);
};
funcOne();
var bau = 1;
function funcTwo() {
bau = bau;
alert('bau is ' + bau);
};
funcTwo();
fiddle

What are the disadvantages of using let instead of var in ES6+? [duplicate]

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?
Scoping rules
The main difference is scoping rules. Variables declared by var keyword are scoped to the immediate function body (hence the function scope) while let variables are scoped to the immediate enclosing block denoted by { } (hence the block scope).
function run() {
var foo = "Foo";
let bar = "Bar";
console.log(foo, bar); // Foo Bar
{
var moo = "Mooo"
let baz = "Bazz";
console.log(moo, baz); // Mooo Bazz
}
console.log(moo); // Mooo
console.log(baz); // ReferenceError
}
run();
The reason why let keyword was introduced to the language was function scope is confusing and was one of the main sources of bugs in JavaScript.
Take a look at this example from another Stack Overflow question:
var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
// and store them in funcs
funcs[i] = function() {
// each should log its value.
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}
My value: 3 was output to console each time funcs[j](); was invoked since anonymous functions were bound to the same variable.
People had to create immediately invoked functions to capture correct values from the loops but that was also hairy.
Hoisting
While variables declared with var keyword are hoisted (initialized with undefined before the code is run) which means they are accessible in their enclosing scope even before they are declared:
function run() {
console.log(foo); // undefined
var foo = "Foo";
console.log(foo); // Foo
}
run();
let variables are not initialized until their definition is evaluated. Accessing them before the initialization results in a ReferenceError. The variable is said to be in "temporal dead zone" from the start of the block until the initialization is processed.
function checkHoisting() {
console.log(foo); // ReferenceError
let foo = "Foo";
console.log(foo); // Foo
}
checkHoisting();
Creating global object property
At the top level, let, unlike var, does not create a property on the global object:
var foo = "Foo"; // globally scoped
let bar = "Bar"; // not allowed to be globally scoped
console.log(window.foo); // Foo
console.log(window.bar); // undefined
Redeclaration
In strict mode, var will let you re-declare the same variable in the same scope while let raises a SyntaxError.
'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo1' is replaced with 'foo2'.
let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared
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++) {
$("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>
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++) {
$("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>
What's the difference between let and var?
A variable defined using a var statement is known throughout the function it is defined in, from the start of the function. (*)
A variable defined using a 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.
Is it safe to use 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 :
How to keep track of browser support
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 moved 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 like var. The main difference is that the scope of a var 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.
let
Block scope
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 (outside of a function)
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
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
Inside a block
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
Inside a loop
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.
Loops with closures
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);
}
Temporal dead zone
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;
No re-declaring
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.
No re-assigning
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().
const obj = Object.freeze({a: 40});
obj.a = 42;
console.log(obj.a); // 40
console.log(obj.b); // undefined
Initializer is required
You always must specify a value when declaring a variable using const.
const a; // SyntaxError: Missing initializer in const declaration
The accepted answer is missing a point:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
In most basic terms,
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 ↓
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
Here is an example of the difference between the two:
As you can see, the var j variable still has a value outside the for loop scope (Block Scope), but the let i variable is undefined outside of the for loop scope.
"use strict";
console.log("var:");
for (var j = 0; j < 2; j++) {
console.log(j);
}
console.log(j);
console.log("let:");
for (let i = 0; i < 2; i++) {
console.log(i);
}
console.log(i);
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.
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.
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.
Global 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.
Examples
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
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.
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
}
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
Function VS block scope:
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
Global 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 over var?
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 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].
This explanation is 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, var variables 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 let and const keywords. While let and const are block-scoped and not function
scoped as var it shouldn’t make a difference while discussing their
hoisting behavior. We’ll start from the end, JavaScript hoists let
and const.
console.log(hi); // Output: Cannot access 'hi' before initialization
let hi = 'Hi';
As we can see above, let doesn’t allow us to use undeclared
variables, hence the interpreter explicitly output a reference error
indicating that the hi variable cannot be accessed before
initialization. The same error will occur if we change the above let
to const
console.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 let or const remain
uninitialized at the beginning of execution while that variables
declared with var are being initialized with a value of undefined.
I added this visual illustration to better help understanding of how are the hoisted
variables and function are being saved in the memory
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;
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;
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
See 'Emulating private interfaces'
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.
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"
Getter and setter with 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)
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.
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.
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.
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
}
As mentioned above:
The difference is scoping. var is scoped to the nearest function
block and let 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.
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.

What scope are variables globally defined by "var"?

In Javascript, if I declare variables with var in javascript outside of a function
var foo = 1;
var bar = 2;
function someFunction() {
....
}
are those variables within the scope of the document or the window? Furthermore, why does this matter? I know that if one declares a variable without var, then the variable is global.
Is there a simple way for me to test whether a variable falls within the scope of the document or the window?
var limits the scope of a variable to the function it was defined in, so variables defined at the top level with var will effectively have global scope.
If you assign a value to a variable that hasn't been scoped with var then it becomes global regardless of where you define it.
There is a good article on javascript scopes here: What is the scope of variables in JavaScript?
When you declare a function in JavaScript, it creates a scope.
When you declare a variable, it must have a var. The var determines what scope it belongs and where it is visible. If it has no var, then it's an "assignment" to a variable and the browser assumes that a variable with that name exists in the outer scopes.
When an assignment happens, the browser searches outwards until it reaches the global scope. If the browser does not see the assigned variable in the global scope, it will declare it in the global scope (which is not good)
for example, take the following as a demo of scope visibility and not actual working functions:
//global variables
var w = 20
var x = 10
function foo(){
function bar(){
//we assign x. since it's not declared with var
//the browser looks for x in the outer scopes
x = 0;
function baz(){
//we can still see and edit x here, turning it from 0 to 1
x = 1;
//redeclaring a variable makes it a local variable
//it does not affect the variable of the same name outside
//therefore within baz, w is 13 but outside, it's still 20
var w = 13;
//declaring y inside here, it does not exist in the outer scopes
//therefore y only exists in baz
var y = 2;
//failing to use var makes the browser look for the variable outside
//if there is none, the browser declares it as a global
z = 3;
}
}
}
//w = 20 - since the w inside was "redeclared" inside
//x = 1 - changed since all x operations were assigments
//y = undefined - never existed in the outside
//z = 3 - was turned into a global
var foo = 1;
window.foo === foo;
JavaScript is a functional language and therefore any variable declared inside the scope of a function is only available in that function.
JS will actually go through each functions scope and look for a variable declared.
function setGlobal() {
bar = 1; // gets set as window.bar because setGlobal does not define it
}
setGlobal();
// logs true and 1
console.log(window.bar === bar, bar); ​
http://jsfiddle.net/kXjrF/
So...
function logGlobal() {
var bar;
console.log( foo, window.foo ) // undefined, undefined
function setGlobal() {
// window.foo is now set because logGlobal did not define foo
foo = 1;
bar = 2; // logGlobal's bar not window.bar
function makePrivate() {
var foo = 3; // local foo
console.log( foo ); // logs 3
}
makePrivate(); // logs 3
}
setGlobal();
console.log( foo, window.foo ); // logs 1, 1
}
as JavaScript has only function scope, variables defined by the var keyword are scoped to the containing function.
you can easily check for global scoping by opening the JavaScript console in the browser (or directly in your code), and typing:
varRef && console.log(varRef)

Categories

Resources