Javascript Closures: Understanding difference between let and var with an example [duplicate] - javascript

This question already has answers here:
What is the difference between "let" and "var"?
(39 answers)
Closed 5 years ago.
Although i understand that let allows you to declare variables that are limited in scope to the block, I encountered a strange difference between let and var while using it with javascript closures. Here is my example:
With let:
function buildFunction() {
var arr = [];
for(var i = 0; i < 3; i++) {
let j = i; //Using let to assign j
arr.push(
function(){
console.log(j);
}
)
}
return arr;
}
var fs = buildFunction();
fs[0]();
fs[1]();
fs[2]();
The above code snippet outputs:
0
1
2
With var:
function buildFunction() {
var arr = [];
for(var i = 0; i < 3; i++) {
var j = i; //Using var to assign j
arr.push(
function(){
console.log(j);
}
)
}
return arr;
}
var fs = buildFunction();
fs[0]();
fs[1]();
fs[2]();
The above code snippet outputs the following:
2
2
2
My question is:
If U am using var inside a block and assigning it a value during execution, shouldn't it work exactly like let and store different copies of j in memory ?
Does javascript handle let and var differently within a closure ?
Any clarifications on this would be highly appreciated.

var scopes to the function; let scopes to a block of code.
In your example j is scoped to the function buildFunction() when you use var. This means your using the 'same' j in every function. When your for loop runs j is set to 0, then 1, then 2. When you then run the console logs your referencing the j that was set to 2, so you get 2 2 2.
When you use let, you scope j to that iteration of the for loop. This means every iteration has a 'different' j, and the console logs print what you expect them to print.
If you were using ES5 and needed to use var, you could replicate the let effect (have it print 0 1 2), by wrapping your original anonymous function with a self-executing function and passed j in as an argument. This would create a new scope for j in the self-executing function whose value is the value of j in the current iteration.
function buildFunction() {
var arr = [];
for(var i = 0; i < 3; i++) {
var j = i; //Using var to assign j
arr.push(
//self executing function
(function(j) { //j here is scoped to the self executing function and has the value of j when it was called in the loop. Any changes to j here will not affect the j scope outside this function, and any changes to j outside this function will not affect the j scoped inside this function.
//original function
return function() {
console.log(j);
}
})(j) //call with the value of j in this iteration
)
}
return arr;
}
var fs = buildFunction();
fs[0]();
fs[1]();
fs[2]();

Related

javascript for loop closure

i think i sort of understand the concept of hoisting when var is used to initialize i in a for loop and the concept of function closure. but why does the following code do not display an error as the final value of i is 4 and arr[3] is the highest index for arr?
why isn't the i in arr[i] linked to the i declared in the for-loop: var i = 0 ? the point of this question is to understand the concept instead of getting the solution
var arr = [1, 2, 3, 4];
for (var i = 0; i<arr.length; i++){
console.log(arr[i])
}
compared to this, the output shows the final value of i (ie 3) three times. i know that let should be used here
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
In the first snippet the highest value is 4 but the max array index is 3 And since your loop is using array index length as it's max value, it doesn't go beyond the max index.
To understand the second example you'll need understand the difference between var and let.
The most basic explanation is var scopped to it's immediate function body (in your case outside of for loop, the same scope as funcs array)
while let scopped inside the enclosure (inside the for loop) and it basically creates create a new variable with each iteration of the loop, while var reuses the same variable.

Why am I able to initialize and assign the variable again inside the for loop using 'let'? [duplicate]

This question already has answers here:
What's the action scope of for-loop in ES6?
(3 answers)
Closed 3 years ago.
for(let i = 0; i < 5; i++) {
console.log(i); //outputs 0, 1, 2, 3, 4
}
HERE ↓ IS THE DOUBT
for(let i = 0; i < 5; i++) {
let i = 2;
console.log(i); //output 2 five times
}
Why am I able to initialize and assign the i variable twice, as I know we can only initialize and assign the variable declared with let once and later can reassign it another value. For example:
let j = 5;
let j = 6;
console.log(j); //error > Identifier 'i' has already been declared
let k = 5;
k = 6;
console.log(k); // output 6
It's like the form of:
let j = 5;
{
let j = 6;
console.log(j); //6
}
console.log(j); //5
for(let i = 0; i < 5; i++) {
let i = 2;
console.log(i); //output 2 five times
}
In second line, just because {} brackets. You are crating a new scope. For more please go through link
let j = 5;
let j = 6;
console.log(j);
Here no scope diffrence, so error.
let k = 5;
k = 6;
console.log(k);
Here both k are in same scope. and you are re-assigning value.
The letdeclaration keeps the variable only within the scope. How Javascript handles scope for for iterative loop is that a new scope is started every iteration. The var keyword will leak the variable into the global scope and maintained every iteration, whereas the let will allow a new i to be declared. For more details, here's a Babel compiled javascript which we can see the actual variables being used and understand this scoping anomaly better! https://stackoverflow.com/a/44367144/8181001

how to access instance's 'this' in collection of objects

I have created a object with constructor function. Then I stored it's 'this' value to a variable.
after creating few objects using 'new' I pushed them to an array. When I access the objects again, all the objects' 'this' value has become the last object created.
var t = function(x){
j = this;
this.u = x;
this.k = function (){
return j.u;
}
}
var g = [];
for(var i = 0; i < 10; i++)
g.push(new t(i));
for(var i = 0; i < 10; i++)
console.log(g[i].k());
this prints ten '9's instead of 0 to 9.
How can I access current instance's 'this' inside function. and access current object's details.
Cause j is a global variable. Do:
let j = this;
So then its part of thr closure.
Use var to localize j's scope
var j = this;
Demo
var t = function(x) {
var j = this;
this.u = x;
this.k = function() {
return j.u;
}
}
var g = [];
for (var i = 0; i < 10; i++)
g.push(new t(i));
for (var i = 0; i < 10; i++)
console.log(g[i].k());
Otherwise, since j's scope is not localized to t, it's last value 9 is returned when you invoke k().
As the previous answers mentioned, you need to use var or let keywords, otherwise the j variable will be bounded to global scope.
var j = this;
One more thing to add, to prevent this kind of mistakes, you can specify:
'use strict'
at the top of your file which will inform you about this mistake by throwing an error your way. Using strict mode prevents you from accidentally binding variables to global scope.

es6 What makes Block-Scoped Variables different?

Looking at http://es6-features.org/#BlockScopedVariables
What a difference between
for (let i = 0; i < a.length; i++) { let x = a[i] … }
and
for (var i = 0; i < a.length; i++) { var x = a[i] … }
I understand in example they move declaration of variables out of block
var i, x, y
for (i = 0; i < a.length; i++)
{
x = a[i] …
}
but is there any reason for it ? why not to include declaration inside the block ? Is it a bad practice or performance hit ?
Just want to understand.
Thanks.
Block scoped variables are simply not available outside their containing block. This is easy to illustrate with an example
for (let i = 0; i < 2; i++) console.info('inner loop i', i);
try {
console.info('after loop i', i);
} catch (e) {
console.error(e.message)
}
for (var j = 0; j < 2; j++) console.info('inner loop j', j);
console.info('after loop j', j);
The primary difference is scope.
Before ES6, you were stuck using var. When using var, variables were defined in the scope of the enclosing function. If there is no function, then they are defined on the global object (typically window). This made it difficult to have two variables named the same, but in different scopes without running into conflicts.
let helps to solve this problem by allowing you to scope variables to the enclosing block, like an if or for loop.
For example, one common issue was nested for loops:
for(var i = 0; i < 4; i++){ // loop 1
console.log(i); // 0
for(var i = 0; i < 4; i++){ // loop 2
}
console.log(i); // 4 - since i was changed by loop 2, loop 1 is now unstable and will skip
}
In the example above, the problem is eliminated using let instead - each i variable is scoped to it's own block.
As for any differences in performance, I can't say. If someone else knows, I'd love to know.

Scope of uninitialized variable in JavaScript

I cannot understand why this code is behaving as it is:
for (var i = 1; i < 3; i++) {
var j;
if (!j) {
j = 1;
} else {
alert("why are we here? j shouldn't be defined but it's " + j);
}
}
(jsFiddle)
If I set j to null and check for null, it works the way I think it should.
This isn't how Java, C#, C++, etc. work, hence, the confusion.
This is because variables in JavaScript are scoped to functions (or the global scope if not within a function). A var is not scoped inside of a loop, and curly braces do not defined a new closure. Basically, the value of j persists after the loop body, and the var does not re-define j as undefined. This is why explicitly setting var j = null; has the expected effect.
Example 1:
A good way to think about this is, any time you declare a variable with var, like this.
function someFunc() {
for(var i = 0; i < 3; i++){
var j;
}
}
The interpreter hoist the variable declarations like so.
function someFunc() {
var i;
var j;
for(i = 0; i < 3; i++){
}
}
Notice that since the var j declaration is hoisted to the top of the function, the declaration actually does nothing within the loop.
Example 2:
However, if you were to initialize the variable with null like this.
function someFunc() {
for(var i = 0; i < 3; i++){
var j = null;
}
}
It would be interpreted like this.
function someFunc() {
var i;
var j;
for(i = 0; i < 3; i++){
j = null;
}
}
Notice how for every loop, j is set to null.
ES6 let keyword:
There is a keyword in ES6 which will create a scope in a loop like this, it is the let keyword. Keep in mind that browser support for the let keyword is poor at this point.
for (var i = 1; i < 3; i++) {
let j;
if (!j) {
j = 1;
} else {
alert("why are we here? j shouldn't be defined but it's "+ j);
}
}
The first time your for loop executes the body of the loop, j is undefined and thus your code sets j=1. On the subsequent iterations of the loop, j is already defined and set to 1 so it goes into your else clause as would be expected.
This occurs because variables defined with var in Javascript are function scoped, not block scoped and if not inside a function, then they are global. So, there is only one variable j in your jsFiddle and each iteration of the for loop uses the same variable (thus inheriting the value from the previous iteration).
It will work if you initialize j = null; inside the body of the for loop because then you're reinitializing it for each iteration rather than using the value from the previous iteration.
ES6 proposes to add the let declaration which would scope to the nearest block. See What's the difference between using "let" and "var" to declare a variable? for more info on let.
There isn't a block scope in JavaScript, only global and function scopes. Although, JavaScript variable statements are so flexible that they will give the programmer the illusion that a block scope could exist, but the truth is that variables declared in JavaScript functions are later hoisted by the interpreter. This means that their declarations are moved to the top of the most-recently declared function. Take this for example:
function test() {
console.log('a test');
for (var i = 0; i < 100; i++) {
var k = i + Math.random();
console.log(k)
}
}
The JavaScript interpreter, internally, will "transform" the code to the following:
function test() {
var i, k; // var declarations are now here!
console.log('a test');
for (i = 0; i < 100; i++) {
k = i + Math.random();
console.log(k)
}
}
It will move all the var declarations to the begining of the most recent function declaration. In your code, the hoisting will create the following code:
// A anonymous function created by jsFiddle to run your script
function () {
var i, j;
for (i = 1; i < 3; i++) {
if (!j) {
j = 1;
} else {
alert("Why are we here? j shouldn't be defined, but it's " + j);
}
}
}
The variable is undefined in the first time, then it gets assigned 1 and then your message is printed.

Categories

Resources