Can anyone explain why document.write part always outputs 10?
function creatFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function () {
return i;
}
}
return result;
}
var funcs = creatFunctions();
for (var i = 0; i < funcs.length; i++) {
document.write(funcs[i]() + "<br />");
}
I think the answer to this question is more thorough than the duplicate question, therefore worth keeping this post.
Sure.
In your for loop, you reference i. What you expect to be happening is that each closure gets a snapshot of i at the time the function is created, therefore in the first function it would return 0, then 1, etc.
What's really happening is that each closure is getting a reference to the external variable i, which keeps updating as you update i in the for loop.
So, the first time through the loop, you get a function that returns i, which at this point is 0. The next time you get two functions which return i, which is now 1, etc.
At the end of the loop, i==10, and each function returns i, so they all return 10.
UPDATE TO ADDRESS QUESTION IN COMMENT:
It's a little confusing since you use i in two different contexts. I'll make a very slight change to your code to help illustrate what's going on:
function creatFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function () {
return i;
}
}
return result;
}
var funcs = creatFunctions();
// NOTE: I changed this `i` to `unrelated_variable`
for (var unrelated_variable = 0; unrelated_variable < funcs.length; unrelated_variable++) {
document.write(funcs[unrelated_variable]() + "<br />");
}
... the functions that you create in your creatFunctions() function all return i. Specifically, they return the i that you create in the for loop.
The other variable, which I've renamed to unrelated_variable, has no impact on the value returned from your closure.
result[i] = function () {
return i;
}
... is not the same thing as result[2] = 2. It means result[2] = the_current_value_of_i
Because you reference i as a variable, which, when the function executes, has the value 10. Instead, you could create a copy of i by wrapping in in a function:
(function (i) {
result[i] = function () {
return i;
}
}(i));
The i that you are returning inside the inner function - is the i that was declared inside the for(var i=0...) therefor - it is the same i for all of the functions in result
and by the time you are calling the functions its value is 10 (after the loop ends)
to accomplish what you wanted you should declare another variable inside the scope of the anonymous function
Because reference to "i" is boxed (enclosed) in your closure that you defined.
result[i] = function () {
return i;
}
And it just keeps reference to the "i" var which keeps changing with each iteration.
UPDATE
This is related to the this keyword function scope which defines variable context.
Hence, in JavaScript, there's no such thing as block scope (for, if else, while, ...).
With that in mind, if you assign var funcs to a function definitioncall, the function body (this) will be bound to the global scope var i inside that function will reach the end of the for loop and stays there in memory. In this case i is 10.
Unfortunately for you, since function scope puts every variable to the top, var i will be global as well.
Although I was completely wrong at first, I stand corrected and took the liberty to create the output of your script in this demo.
Related
To my understanding, if the loop variable of a for loop is defined with var, then any change on that variable is applied globally. for example:
var printNumTwo;
for (var i = 0; i < 3; i++) {
if (i === 2) {
printNumTwo = function() {
return i;
};
}
}
console.log(printNumTwo());
In the above code, 3 will be printed into the console. Because in the last iteration the variable i will equal to 3. Therefore when printNumTwo is called, the update i will be returned.
However if I use let this is not the case and the following behavior happens:
let printNumTwo;
for (let i = 0; i < 3; i++) {
if (i === 2) {
printNumTwo = function() {
return i;
};
}
}
console.log(printNumTwo());
The above code will print 2.
let printNumTwo;
for (let i = 0; i < 3; i++) {
if (i === 2) {
printNumTwo = function() {
return i;
};
i = 3;
}
}
console.log(printNumTwo());
however, the above code prints 3.
What is the reason for this?
source: The first two code blocks are from here.
In the first example, var scopes i to the function, so at the end of the loop i++ changes i from 2 to 3 and the loop stops. The only i is now 3 and printNumTwo returns i which is 3.
In the second example let scopes i to the block, so the function assigned to printNumTwo closes over it. The last time the body of the loop runs, i is 2 and printNumTwo returns i which is 2. (A new i is created with the value 3 but no function is created that uses it).
In the third example, let scopes i to the block and the same thing happens except you have i = 3; which changes every i to 3. So the last function that is created (as well as the previous ones which are overwritten) returns i which is 3.
however, the above code prints 3. What is the reason for this?
Because you assign 3 to the i variable that printNumTwo closes over. It doesn't matter that the assignment happens after printNumTwo is created, only that it is the variable that printNumTwo is using.
The difference between var and let in for loops is that a new variable is created for the body of the loop on each loop iteration with let. But you're assigning to that variable within the loop body, so the function closing over (printNumTwo) it sees that value later when you call it.
It's exactly like this:
function create(i) {
// This function closes over `i`
const fn = function() {
return i;
};
// This modifies the `i` it closes over
++i;
return fn;
}
const printValue1 = create(1);
console.log(printValue1()); // 2 (1 + 1)
const printValue27 = create(27);
console.log(printValue27()); // 28 (27 + 1)
In response to a comment on the question, you've said:
same thing also happens in the second code block (i++ in the update section in for loop ) but the answer is 2
Ah! Now I see why there's a misunderstanding. You're right that there's an update to i — but it's not the i that printNumTwo closes over.
The update section of the for is performed on the new variable for the next iteration at the start of the next iteration, not on the one for the iteration that just finished at the end of the iteration. It does this:
Create a new iteration variable (let's call it iNew)
Assign the value of the old iteration variable (let's call it iOld) to the new variable (iNew = iOld)
Run the update code using iNew (iNew++)
That's why i (the old one) remains 2; it's the new one that becomes 3.
Scope of var is the function and scope of let is the block.
So when assigning value to the printNumTwo function, the value of i is still 2. doesn't matter what happens to i later.
The behavior of var and let you can understand from this example.
for(var i=0; i<3; i++){
setTimeout(() => console.log('var', i))
}
for(let i=0; i < 3; i++){
setTimeout(() => console.log('let',i))
}
I'm trying to understand closure, I thought I understood it until I came across the following solution:
I don't understand how this will alert 0, 1, 2, 3, 4, 5
var next = (function(){
var r = 0;
return function(){
return r++;
}
})()
for (var i = 0; i<=5; i++){
alert(next());
}
I understand how the first iteration works, just having next, the function will execute and return the return function, then the extra () after next will execute that returned function.
I also understand that the return function when executed will have access to the variable r because of Closure.
However I don't understand how on the next iteration, the state of r will persist.
On the next iteration when next() get run again, the r variable will be inside the function scope and won't get affected by anything in the global, how is it that it knows the state of the r in the previous next(). Even if it does, wouldn't var r = 0 reset r back to 0?
The outer IIFE runs only once, as indicated by the () at the end:
var next = (function(){
var r = 0;
return function(){
return r++;
}
})()
^^
So, when the interpreter comes across the var next = ... line, the IIFE runs, creates the r variable, and then returns the following function:
function(){
return r++;
}
next is assigned to the result of calling the IIFE, and the IIFE returns that function. So, in the future, when you call next, you're calling that (small) function above - calling next doesn't call the whole IIFE again, and there's only ever one binding for the r variable.
Were next an uninvoked function that created an r, you'd be right, every invocation of next would create a new function with a new binding for r:
var next = function(){
var r = 0;
return function(){
return r++;
}
}
for (var i = 0; i<=5; i++){
var theFn = next();
console.log(theFn());
}
I have been working through this article on closures: Understand Javascript Closures with Ease
The final example deals with a closure inside a for loop.
I understand why an IIFE is used to capture the current value of 'i' as 'j'. What I don't understand about this example is why there is a 2nd, inner IIFE wrapped around the return statement. (My comment is in caps in the code below).
The code seems to work just the same without the inner IIFE. See CodePen here.
Is this inner function required for some reason, or is this just an oversight by the author?
function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function (j) { // the j parametric variable is the i passed in on invocation of this IIFE
return function () { //<--WHY DOES THIS INNER FUNCTION NEED TO BE HERE?
return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array
} () // BY adding () at the end of this function, we are executing it immediately and returning just the value of uniqueID + j, instead of returning a function.
} (i); // immediately invoke the function passing the i variable as a parameter
}
return theCelebrities;
}
var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0},{name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id); // 100
var cruiseID = createIdForActionCelebs [1];
console.log(cruiseID.id); // 101
var willisID = createIdForActionCelebs[2];
console.log(willisID.id); //102
The inner function, as you observed, has no practical effect. It doesn't make sense to use it.
It appears to be a holdover from the previous example in the article where a function was returned instead of a number.
I was going through a javascript book and have encountered the following code on closure
function constFunc() {
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs[i] = function () {
return i;
}
}
return funcs;
}
var f = constFunc();
f[5]();
The output was not 5.
The explanation given was "nested functions do not make private copies of the scope".
i would be grateful if could someone explain the above statement.
When you call constFunc the loop runs and assigns a function to each element of the funcs array. Those functions each return the value of i, which changes on each iteration of the loop from 0 up to 9.
That means that once constFunc has finished executing i will have the value of 9. When you call one of the functions in the array it returns the value of i which as we've just seen is now 9. The value of i is not captured on each iteration of the loop.
You can force it to be captured via a closure in the loop:
for (var i = 0; i < 10; i++) {
(function (i) {
funcs[i] = function () {
return i;
};
}(i));
}
In this case you are effectively creating a new variable with the same value as i on every iteration of the loop. You can see that in action here.
By passing i into a function you end up copying the value of it. So on the first iteration i has the value 0. We pass that value into the new function (the argument i is a different variable to the i declared in the loop initialiser) and then return that value from the array function. This way, each array function is returning a different i, instead of each returning the same i.
WHy did you expect the output of 5? RIght, because at the time the function was created, i was 5. Now, as we can see, the output is not 5 (but, if I didn't miss anything, 9). Now why is that?
The iinside the closure is not evaluated when the function is created, but when it runs. Now this is where the statement you qoted comes into play: there is only ONE i- the one created inside constFunc. All closures work on that one. After constFunc si done working, that i has, obviously, the value of 9 (becaus that's where the loop stops). SO all closures now output 9.
A simpler example:
function outer () {
var i = 0;
function inner () {
return i;
}
i = 99;
}
outer();
inner(); // <- return 99, not 0.
There is one variable called i.
There are 10 functions that return i.
Each one of those functions returns the current value of the single i variable.
Here in this loop:
for(var i=0;i<10;i++)
{
funcs[i]=function(){return i;}
}
due to closure, inside function you are accessing parent level variable of i, which after complete loop will be 10. So after loop when you call these function it will return you value of i which is 10.
You can solve it by passing variable as parameter, as parameter create the new copy of variable which maintain its state even on completion of loop.
var myFunctions= [];
function createMyFunction(i) {
return function() {
console.log("My value: " + i);
};
}
for (var i = 0; i < 3; i++) {
myFunctions[i] = createMyFunction(i);
}
I've seen programmers assign events listeners inside loops, using the counter. I believe this is the syntax:
for(var i=0; i < someArray.length; i++){
someArray[i].onclick = (function(i){/* Some code using i */})(i);
}
Could someone please explain the logic behind this, and this weird syntax, I've never seen this:
(function(i))(i);
Many thanks for your time and patience.
The (function(i))(i) syntax creates an anonymous function and then immediately executes it.
Usually you'll do this to create a new function every time through the loop, that has its own copy of the variable instead of every event handler sharing the same variable.
So for example:
for(int i = 0; i < 10; i++)
buttons[i].click = function() { doFoo(i); };
Often catches people out, because no matter what button you click on, doFoo(10) is called.
Whereas:
for(int i = 0; i < 10; i++)
buttons[i].click = (function(i){ return function() { doFoo(i); };)(i);
Creates a new instance of the inner function (with its own value of i) for each iteration, and works as expected.
This is done because JavaScript only has function scope, not block scope. Hence, every variable you declare in a loop is in the function's scope and every closure you create has access to the very same variable.
So the only way to create a new scope is to call a function and that is what
(function(i){/* Some code using i */}(i))
is doing.
Note that your example misses an important part: The immediate function has to return another function which will be the click handler:
someArray[i].onclick = (function(i){
return function() {
/* Some code using i */
}
}(i));
The immediate function is nothing special. It is somehow inlining function definition and function call. You can replace it by a normal function call:
function getClickHandler(i) {
return function() {
/* Some code using i */
}
}
for(var i=0; i < someArray.length; i++){
someArray[i].onclick = getClickHandler(i);
}