The function simply executes with the alert using the current value of i.
var test1 = function() {
var i = "Original value";
alert(i);
i = "The value has changed";
};
The function inside the timer runs after test2 has finished. The inner function maintains a reference to i. When the alert is executed, the value of i has changed.
var test2 = function() {
var i = "Original value";
setTimeout(function() {alert(i)}, 0);
i = "The value has changed";
};
The function inside the timer runs after test3 has finished even though the delay is set at 0 ms. This is because, in browsers at least, JavaScript is single threaded; the running function has to finish before the timer gets a look in. The inner function maintains a reference to i. When the alert is executed, the value of i has changed.
var test3 = function() {
var i = "Original value";
setTimeout(function() {alert(i)}, 0);
for (var wait = 0; wait < 500000; wait++) {
document.getElementById("counter1").innerHTML = wait;
};
i = "The value has changed";
};
0
The outer function runs immediately and finishes. It sets an event handler on the button. The event handler will run later when the button is clicked. The handler maintains a reference to i even though the outer function has finished. When the alert is called, the value of i will be its last value in the outer function.
(function() {
var i = "Original value";
document.getElementById("btn3").onclick = function() {
alert(i);
};
i = "The value has changed";
})();
The outer function runs immediately. As the value of i loops, each button is referenced in turn and event handlers are set. The event handlers maintain a reference to i. When the alert method is called, the last value of i from the outer function is used. That value is 4 - the value that stopped the loop running.
(function() {
for (var i = 1; i < 4; i++) {
document.getElementById("btn4_" + i).onclick = function() {
alert(i);
};
};
})();
The outer function runs immediately. As the value of i loops, each button is referenced in turn and event handlers are set to the return value of an inner function which is called for each loop value. That function accepts i as a parameter. The function returned maintains a reference to the parameter. The parameter for each returned function is different so this time the buttons report back different i values.
(function() {
for (var i = 1; i < 4; i++) {
document.getElementById("btn5_" + i).onclick = function(i) {
return function() {alert(i)};
}(i);
};
})();
The i for the button id and for the alert are now both inside the inner function. The button id uses the i parameter immediately. The alert maintains a reference to the i parameter via a closure.
The need to return a function from a function to set the event handler is avoided. The inner self-executing anonymous function can be expanded to include an arbitrarily large block of code. That whole function block will have access to the i parameter and any nested functions can maintain a reference via a closure.
(function() {
for (var i = 1; i < 4; i++) {
(function(i){
document.getElementById("btn6_" + i).onclick = function() {alert(i)};
})(i);
}
})();
The body of the loop is still wrapped in a self-executing anonymous function. However, this time the loop index i is not passed in as a parameter. For each iteration of the loop the inner function executes and the button gets its id from i. The alert method is assigned as an event handler and so its reference to i is maintained in a closure. When called, i will have reached 4. All buttons report 4.
(function() {
for (var i = 1; i < 4; i++) {
(function(){
document.getElementById("btn7_" + i).onclick = function() {alert(i)};
})();
}
})();
The body of the loop is still wrapped in a self-executing anonymous function. The loop index i is not passed in as a parameter. For each iteration of the loop the inner function executes and the button gets its id from i.
A new variable, ix, is created within the scope of the inner function. For each iteration of the loop the value of ix is set to i. Each event handler now maintains a reference to ix via a closure. However that reference isn't to some general ix but to the specific ix from within the executed inner function. There are three ix variables created, each in its own private function scope.
The buttons report the correct values.
This example is similar to Closures and Loops 3. There, the local variables in the private function scope are the parameters passed in. Here, they are explicitly created as a new variable. If this example used ix for the button id, the two examples would be virtually identical.
(function() {
for (var i = 1; i < 4; i++) {
(function(){
var ix = i;
document.getElementById("btn7_" + i).onclick = function() {alert(ix)};
})();
}
})();
The User constructor function is passed a properties object. The aim is to iterate over the properties passed in and create a 'getter' method for each that can later be used to retrieve the values of those properties.
The k for the getter method name is set appropriately. The k in properties[k], however, is within a closure holding the loop value. When the getter method is called it will return the last value of k. Thus all getters will return that last value.
There is a more fundamental problem. This approach will not work at all! The this of the getter no longer represents the current User object instance. Now that it is wrapped inside an inner function, the context is broken. In fact, because the inner function is not a constructed object or a method of an object, the this reference is null and so is set to the global object!
In the example below, clicking button 'Test 9b' alerts the result of the global getname and getage functions. Clicking 'Test 9a' creates a new User object but its alert fails. There are no user.getname or user.getage functions. Because this points to the global object, the original global functions have been overwritten instead. Clicking 'Test 9b' will show the result of calling the new global functions.
function User(properties) {
for (var k in properties) {
(function() {
this["get" + k] = function() {return properties[k];};
})();
}
}
(function() {
document.getElementById("btn9a").onclick = function() {
var user = new User({name:"Bob", age:34});
alert("Name: " + user.getname() + ", Age: " + user.getage());
};
document.getElementById("btn9b").onclick = function() {
alert("Name: " + getname() + ", Age: " + getage());
};
})();
The User constructor function is passed a properties object. The aim is to iterate over the properties passed in and create a 'getter' method for each that can later be used to retrieve the values of those properties.
This time the this["get" + k] is outside of the executed inner function. this now points to the User instance as required and k is set directly from the loop. The k in properties[k] now references a parameter, rather than the loop k. All getters get the right values.
The only problem here is the rather clunky function returning a function which returns a function. It would be nice if so many nested functions could be avoided.
function User(properties) {
for (var k in properties) {
this["get" + k] = function(k) {
return function() {return properties[k]};
}(k);
}
}
(function() {
document.getElementById("btn10").onclick = function() {
var user = new User({name:"Bob", age:34});
alert("Name: " + user.getname() + ", Age: " + user.getage());
};
})();
The User constructor function is passed a properties object. The aim is to iterate over the properties passed in and create a 'getter' method for each that can later be used to retrieve the values of those properties.
To avoid the multiple nested functions, the self-executing anonymous function wraps the whole loop once again, creating a local scope. To avoid the problem with this in the new scope, the reference to the User instance is assigned to the variable that, which is used in the inner function.
Unfortunately, properties[k] once again refers to the outer loop via a closure. When called, all getters will return the same value.
function User(properties) {
var that = this;
for (var k in properties) {
(function() {
that["get" + k] = function() {return properties[k]};
})();
}
}
(function() {
document.getElementById("btn11").onclick = function() {
var user = new User({name:"Bob", age:34});
alert("Name: " + user.getname() + ", Age: " + user.getage());
};
})();
The User constructor function is passed a properties object. The aim is to iterate over the properties passed in and create a 'getter' method for each that can later be used to retrieve the values of those properties.
This example solves the loop reference problem, the context problem and the multiple nested returns. It is the same as 'Closures and this 3' but the loop value k is passed into the self-executing anonymous function as a parameter, making the correct value available to the getter name and to the method when called later.
function User(properties) {
var that = this;
for (var k in properties) {
(function(k) {
that["get" + k] = function() {return properties[k]};
})(k);
}
}
(function() {
document.getElementById("btn12").onclick = function() {
var user = new User({name:"Bob", age:34});
alert("Name: " + user.getname() + ", Age: " + user.getage());
};
})();
The User constructor function is passed a properties object. The aim is to iterate over the properties passed in and create a 'getter' method for each that can later be used to retrieve the values of those properties.
function User(properties) {
for (var k in properties) {
(function() {
this["get" + k] = function() {return properties[k];};
}).call(this);
}
}
(function() {
document.getElementById("btn13a").onclick = function() {
var user = new User({name:"Bob", age:34});
alert("Name: " + user.getname() + ", Age: " + user.getage());
};
document.getElementById("btn13b").onclick = function() {
alert("Name: " + getname() + ", Age: " + getage());
};
})();
The User constructor function is passed a properties object. The aim is to iterate over the properties passed in and create a 'getter' method for each that can later be used to retrieve the values of those properties.
function User(properties) {
for (var k in properties) {
(function(k) {
this["get" + k] = function() {return properties[k];};
}).call(this, k);
}
}
(function() {
document.getElementById("btn14a").onclick = function() {
var user = new User({name:"Bob", age:34});
alert("Name: " + user.getname() + ", Age: " + user.getage());
};
document.getElementById("btn14b").onclick = function() {
alert("Name: " + getname() + ", Age: " + getage());
};
})();