Looks like for some reason my code cannot keep track of a local variable i being incremented and it always return its initial value 0.
Here is JSFiddle where I played around http://jsfiddle.net/ou2uxwn5/20/
var counter = function(){
var i = 0;
console.log(i);
return function(){
i++;
return i;
};
};
Not sure if garbage collector eats it up, though behavior is the same with var calls being defined too (I would expect this to create reference and not to throw local variable i into trash bin).
What is wrong with the code and why?
You should execute the first function immediately to avoid any confusion down the line. This will return a new function and assign it to counter.
var counter = (function(){
var i = 0;
console.log(i);
return function(){
i++;
return i;
};
})(); // logs 0 in the console
Calls to counter() will increment i and return it's value.
console.log(counter()); // logs 1 in the console
console.log(counter()); // logs 2 in the console
I suggest you read about closures to get a better understanding of how they work. Here is a good place to start.
problem solved:
1) should call "calls()" instead of "counter()"
2) moved console.log(i) into return statement.
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 am new to js and I am experimenting with closures. There is something I can't get my head around.
If I write
var uniqueInteger = (function() { // Define and invoke
var counter = 0; // Private state of function below
return function() { return counter++; };
}());
and I repeatedly call the function as uniqueInteger() I obtain 0,1,2,3 and so on. It seems then that counter is not destroyed after the function is called. Where is it stored since it is not a global object?
Now, if I slightly change the code and define uniqueInteger as
var uniqueInteger = (function() { // Define and invoke
var counter = 0; // Private state of function below
return function() { return counter++; };
});
and I repeatedly call uniqueInteger()() the value which I obtain is constantly 0. How is that possible?
Where is it stored since it is not a global object?
In the scope created by the closure.
Now, if I slightly change the code … the value which I obtain is constantly 0. How is that possible?
Because every time you call var counter = 0; in the second chunk of code, you rerun the expression var counter = 0; (you also create a new scope and return a new function which you immediately call because you had ()()).
In the first chunk of code you ran that function once and assigned its return value to uniqueInteger, so the expression that set counter to 0 was only evaluated once.
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.
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);
}