Getting around variable hoisting for callbacks in javascript [duplicate] - javascript

This question already has answers here:
Javascript infamous Loop issue? [duplicate]
(5 answers)
Closed 8 years ago.
My understanding of Javascript is that variables have function scope, not block scope, so variables declared inside a block are hoisted to the function above it. This article gives the following code example:
var arr = [1, 2, 3, 4]
for(var i = 0; i < arr.length; i++) {
var entry = arr[i];
setTimeout(function() {
console.log(entry);
}, 100);
}
The article states the the code gets translated into the following, and so the variable entry is common between each iteration. The result is that 4 is logged to the console four times
var arr, i, len, entry;
arr = [1, 2, 3, 4]
for(i = 0; i < arr.length; i++) {
entry = arr[i];
setTimeout(function() {
console.log(entry);
}, 100);
}
My question is, how should I rewrite the code if I want each call to setTimeout to log a different value from arr?

You can change the setTimeout to:
setTimeout(
(function()
{
var entry = arr[i];
return function() {console.log(entry);}
}
)()
);
That way, you create a new scope, and entry is in that scope.
You can also do:
(function(e)
{
setTimeout(function(){console.log(e);});
}
)(entry);
Which also creates a new scope, in which e lies.
Yet another option:
setTimeout(
(function(e)
{
return function() {console.log(e);};
}
)(entry);
);
In all cases, you use an anonymous function that you call immediately, so the only side-effect is that you are creating a new scope.
(edit: reformated a bit for readability, and added a bit more explanations)

Related

Why does i in "for (let i..." behave like block scope sometimes but not others [duplicate]

This question already has answers here:
Explanation of `let` and block scoping with for loops
(5 answers)
Closed 3 years ago.
Consider the following code snippet:
for (let i = 0; i < 5; i++) {
i+=1;
setTimeout(() => console.log(i), 100);
}
console.log('after for loop');
If let i were creating a new block scope variable with each iteration, I would expect it to output:
1
2
3
4
5
because, being a new block scope variable, i+=1; would only make changes to my local copy of i. Also, if i was a new block scope variable, that would explain why the setTimeout callback doesn't log "6" 3 times (as it does if let i is changed to var i).
In case it helps, here is what I'm imagining it would be doing under the hood if it were creating a new block scope variable for each iteration:
for (let I = 0; I < 5; I++) {
let i = I;
i+=1;
setTimeout(() => console.log(i), 100);
}
console.log('after for loop');
However, the top snippet actually outputs:
1
3
5
which would make sense if i were shared between all iterations except if i were shared between all iterations why would the setTimeout callback not print the same number 3 times?
In short my question is:
Why, in the top snippet, is i+=1; both updating the loop variable as if i in i+=1; is not a local copy for each iteration and also behaving in the setTimeout callback as if i is a local copy for each iteration.
When the variable declared in the for loop declaration gets reassigned within the loop body, that reassignment will persist for the next iteration. It's a lot clearer if you look at how Babel transpiles it:
for (let i = 0; i < 5; i++) {
i+=1;
setTimeout(() => console.log(i), 100);
}
console.log('after for loop');
results in
"use strict";
var _loop = function _loop(_i) {
_i += 1; // <---------------
setTimeout(function() {
return console.log(_i);
}, 100);
i = _i; // <---------------
};
for (var i = 0; i < 5; i++) {
_loop(i);
}
console.log("after for loop");
If you change or log i outside of the synchronous execution of the for loop body, it will (essentially) refer to the _i above, acting like a completely independent block-scoped variable. But if you change i inside the synchronous for loop body, the next iteration will start with the changed i.

Javascript iterator design pattern [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
How do JavaScript closures work?
(86 answers)
Closed 7 years ago.
I am confused why the following code snippet has (i) at the end of it:
for (var i = 0; i < 10; i += 1) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 1000);
})(i);
}
Ive seen it in production code Ive worked on--I just can intuit why it's necessary.
You are defining an inline function, so you could force the i parameter to be in the local scope at the time of the execution of the console.log statement. By adding the parameter, you are creating a self executing function. Similar would be the following statements, which might be better to read...
function logmeWithTimeOut(value) {
setTimeout(function() { console.log(value); }, 1000);
}
for (var i = 0; i < 10; i++) {
logmeWithTimeout(i);
}
Though I might prefer
function logmeWithTimeout(value) {
console.log(value);
}
for (var i = 0; i < 10; i++) {
setTimeout(logmeWithTimeout.bind(undefined, i), 1000);
}
It forces the i to be in local scope, otherwise your log would print only 11
Because you are defining a function inside the parenthesis, and then you are calling it passing i as parameter.
If you didn't put the (i) there, you would just define a function but never call it.

How can I write a JS callback function that remembers the old value of a changing variable? [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
Here's a simplification of my JS program. mylist is an array of strings and MyAsync is a function that takes an object and arranges for the supplied callback to be called at a later time.
for (var myindex = 0; myindex < mylist.length; myindex += 1) {
MyAsync(mylist[myindex], function () { alert(myindex); });
}
When there's ten items in mylist, the result is two alert messages each showing "10". Okay, it's clearly using the value of myindex after the loop finishes. So I make a slight change...
for (var myindex = 0; myindex < mylist.length; myindex += 1) {
var copyindex = myindex;
MyAsync(mylist[myindex], function () { alert(copyindex); });
}
Now, each alert shows "9".
How do I please arrange for the callback function to know what myindex was at the time MyAsync was invoked?
Asynchronously, billpg.
Yeah, as the comments and not making functions in a loop for the reason you are experiencing.
We can use a recursive function instead :
var len = mylist.length;
function asyncLoop(index) {
MyAsync(mylist[index], function () {
if(index < len) {
asyncLoop(++index);
}
});
}
asyncLoop(0);
As comments , - firing them all off at the same time* ( *- quick as a loop can run ). and keeping track of the array count ...
function asyncDo(index) {
MyAsync(mylist[index], function () {
/* we have access to the array index here */
console.log(index);
});
}
for ( var i=0, len=mylist.length; i<len; ++i) { asyncDo(i); }
You can use closures to do this: here is some code that I think demonstrates what you want, printing out 0 .. 9.
var funcs = [];
for(var i = 0; i < 10; i++){
funcs.push( (function(j){
return function(){
console.log(j);
};
})(i));
}
funcs.forEach(function(f){
f();
});
The idea is that in returning a function to be added to the list, the value of i is captured when that function was constructed from the parameter to the outer function.
Here's an SO post that helped me with this: JavaScript closures vs. anonymous functions

Understanding closures in JavaScript [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 9 years ago.
Consider this
fs=[];
for(var i = 0; i < 3; i++){
fs.push(function(){
return i;
});
}
console.log(fs[0](),fs[1](),fs[2]())
I thought that i would be in the closure of the function so that this would print "0 1 2", but nope... it prints "3 3 3".
Why doesn't this work? And is there any way to make this work?
You are actually pushing three instances of the same anonymous function into your 'fs' array. Then your log statement calls that function three times. At the time you are calling it, the loop has completed and the value of 'i' within the closure is 3, so that's what is returned.
I'm not sure what you mean by "work", but maybe something like this is what you mean:
fs = [];
function closureFunc(myVal) {
return function () {
return myVal;
}
}
for (var i = 0; i < 3; i++) {
fs.push(closureFunc(i));
}
console.log(fs[0](), fs[1](), fs[2]());
This creates three separate closures where the function you are pushing has the scope of the declaring function "closureFunc". Within those closures the myVal is set to the value that was passed in.
Javascript doesn't have a notion of block scope, it only has function scope. Hence the placement of the var i is meaningless in terms of lifetime. It may as well have been the first line in the method.
In order to fix this you need to declare a new scope that holds a separate i value
fs=[];
var makeFunc = function(j) { return function() { return j; } }
for(var i = 0; i < 3; i++){
fs.push(makeFunc(i));
}

JavaScript () following } [duplicate]

This question already has answers here:
standalone parentheses in javascript [duplicate]
(5 answers)
Closed 8 years ago.
I am studying JavaScript and I sometimes see something like this
function functionname()
{
// Some statements
} () ;
What does () following } mean?
Thanks a lot, experts on Stack Overflow
That is an IIFE (immediately invoked function expression). It is used to create a new scope. For example:
var x = 10;
(function() {
var x = 5;
}());
alert(x); // still 10
All it does is define and call the function at the same time.
IIFEs are also used to "save" the value of a variable. For example, this code won't work:
for (var i = 0; i < buttons.length; i ++) {
buttons[i].onclick = function() { alert(i) }
}
Every button will alert the last index when you click it, because after the loop is done i is buttons.length. To fix it, you would use an IIFE:
for (var i = 0; i < buttons.length; i ++) {
buttons[i].onclick = (function(i) {
return function() {
alert(i)
}
})(i)
}
More info on IIFEs
It calls the function.
var foo = function () { return 1; }();
alert(foo); // 1
There are lots of ways to define a function, when you do it like this ...
(function functionname(innerArguments){
// Some statements;
})(outerArguments);
... the function is executed as soon as this piece of code is interpreted;
its the same as:
function functionname(innerArguments){
// Some statements;
};
functionname(outerArguments);

Categories

Resources