confused by interaction between function parameter, closures, and scope chain - javascript

The code in question:
function addLinks () {
for (var i=0, link; i<5; i++) {
link = document.createElement("a");
link.innerHTML = "Link " + i;
link.onclick = function (num) {
return function () {
alert(num);
};
}(i);
document.body.appendChild(link);
}
}
window.onload = addLinks;
My problem is since the returned function is a closure and variable num is the global variable each time the function executes num should hold the current value replacing the old one and should automatically update with that value everywhere. But this isn't happening -- when I click each link I get a different value. Why is this happening?

num is closed over by the anonymous function. The function (num) { return function () {}}(i) passes i as it is to the inner function and returns a new function based on whatever i's value is at the time.
If you want the click callback to always alert the maximum value of i, it's actually even easier:
link.onclick = function () {
alert(i);
}
function (var varname) is simply invalid syntax.

Think about it: what if you had three links like so:
0
1
2
You want them to alert out their number when you click on them, so you do it like this:
var links = $('a');
for (var i = 0; i < links.length; i++) {
links[0].onclick = function () {
alert(i);
}
}
At first glance, you'd expect that, for example, since you assigned the click handler on the first link when i = 0, it'll alert 0 when you click it. However when you click it, it'll actually alert 3.
You said it yourself, your code is creating a closure. What the code above does is that it's assigning a function handler to the click event of each link. Each of those function handlers is maintaining a reference to the variable i (note: not it's current value!).
At the point when you assign the function handler, it actually doesn't evaluate what value i has (because it doesn't need it). When you click, aha, that's when it checks what value i has and alerts it.
By the time you click a link, your for loop will be long finished, with i = 3, and that's what your click handler alerts.

Please look at the (i) after your function. This type of notation is for self-invocating functions only. It's as if you are setting link.onclick = a number, whereas it expects a function. You can simply use the following.
link.onclick = function (event) {
event.preventDefault();
alert(i);
};
Please note that click functions receive the "event" as a parameter by default. Make sure you call the preventDefault() method of the event otherwise it will bubble up the DOM and trigger a postback due to the nature of the anchor element.

This is strange and disagrees with what I thought to knew about closures. With the only change (in the first version of addLinks in the question):
link.onclick = function (num)
to
link.onclick = function ()
You get the expected result, that is, the actual value of the global num variable is alerted any time when a link is clicked.
It probably has to do with how the interpreter saves scope variables that are referenced within a closure when it encounters that closure. When a variable is referneced in a closure, the closest occurrence of that variable is searched for going upwards from the current scope.
While in the first case it is defined as a parameter (to the function that is called after declaration multiple times) it has different value each time so a different scope value is "remembered" by each closure.
In the 2nd case, the only occurrence found is in the global scope which results in the actual value of num is being used regardless which handler is called.

You haven't explained where the variable num has come from or how it's used. I'm guessing you mean to alert the current value of i. The click handler takes an event object as a parameter so I would try it like this:
function addLinks () {
for (var i=0; i<5; i++) {
var link = document.createElement("a");
link.innerHTML = "Link " + i;
link.onclick = function (event) {
alert(i);
};
document.body.appendChild(link);
}
}
window.onload = addLinks;

Related

Newbie - Manipulating closures and For Loops

The answer to why this code is not working is because: The problem with this method is is you're not providing a function as the second argument. You're calling a function - that function is making things blue. It's not returning a function that is making things blue. So you're making things blue immediately and, when the click happens, there's nothing to call, because the function you called returned nothing.
But I'm lost after the first sentence. could someone dumb this down? perhaps a visual explanation of the code would help!
Code: https://jsfiddle.net/2yfj89af/1/
var submitSOBox = document.getElementsByClassName("submitSOBox");
for (i = 0; i < submitSOBox.length; i++) {
submitSOBox[i].addEventListener('click', SOBoxColor(i));
}
function SOBoxColor(i) {
submitSOBox[i].style.backgroundColor = "blue";
}
The reason explained: you have this line:
submitSOBox[i].addEventListener('click', SOBoxColor(i));
This calls addEventListener which expects a function as the last argument. You provide SOBoxColor(i), which is not a function, but a value returned by a function -- you call it. To pass a function, you would need to pass something like SOBoxColor, so without calling it. This is needed because your browser needs to know what to call when the click event happens.
Now, passing just SOBoxColor will not do what you want. Yes, it will make that SOBoxColor gets called when a click happens, but you will not have the i value passed to it.
To achieve that, you need a modified version of your function that somehow has the i value pre-filled as argument. Luckily there is a way to create such a modified function with the bind method, which returns the same function, but with some things bound to it:
submitSOBox[i].addEventListener('click', SOBoxColor.bind(submitSOBox[i], i));
The first argument of bind determines what the special this object will represent when SOBoxColor is called, and the second argument is what i will be.
Now, this will work.
But you can do it even nicer if you would use the this keyword in your SOBoxColor function, because then you don't even need the i parameter:
function SOBoxColor() {
this.style.backgroundColor = "blue";
}
... and your binding can also do without the i argument:
submitSOBox[i].addEventListener('click', SOBoxColor.bind(submitSOBox[i]));
It can still be done more concisely, because when the click happens, the browser will already provide a nice service to us: it sets the this keyword to the element to which you added the event handler, so in the end it is not even necessary to bind submitSOBox[i] to it explicitly. This will work also:
submitSOBox[i].addEventListener('click', SOBoxColor);
... provided you changed the SOBoxColor function to work with this.
When you add the event listener like you are:
submitSOBox[i].addEventListener('click', SOBoxColor(i));
It is calling the function SOBoxColor(), expecting it to return a function that will be called when the event listener is triggered.
By return a function, I mean like this:
function SOBoxColor(){
return function(){
alert("hello")
//Do stuff
}
}
If you replaced the current function with the one above, it would alert "hello" whenever the click listener is triggered.
How would you fix this?
Simply put the functions name in it, without calling it:
submitSOBox[i].addEventListener('click', SOBoxColor);
The problem you are going to have now is, with your current function, SOBoxColor:
function SOBoxColor(i) {
submitSOBox[i].style.backgroundColor = "blue";
}
You are sending the number as a parameter to select the appropriate element, which you can't do anymore.
However, this isn't a problem, since you were selecting the element incorrectly in the first place.
Instead of selecting it with it's index in the HTMLCollection submitSOBox, just reference the current element with the this keyword:
function SOBoxColor() {
this.style.backgroundColor = "blue";
}
So, your complete code should now look like:
var submitSOBox = document.getElementsByClassName("submitSOBox");
for (var i = 0; i < submitSOBox.length; i++) {
submitSOBox[i].addEventListener('click', SOBoxColor);
}
function SOBoxColor() {
this.style.backgroundColor = "blue";
}
You're assuming calling a function must be done by
functionName();
But it's not the only way of calling functions. You could input a function as an argument, use the call method: functionName.call(null,args), etc.
What functionName(); does is make Javascript go, "Hey, I need to find out what that function returns." So if functionName is like
function functionName(){
console.log("hello!");
return undefined;
};
Javascript will try to simplify everything. It will run through your function, console log "hello!" while it's busy working, and in the very end figure out what your function returns:
undefined;
Think of it like simplifying math expressions back in algebra class or something.
In your case, you're telling the click event to call whatever is returned by SOBoxColor(i). So if SOBoxColor looked like this:
function SOBoxColor(i) {
submitSOBox[i].style.backgroundColor = "blue";
return 9000;
}
Your click event would look like this:
for (i = 0; i < submitSOBox.length; i++) {
submitSOBox[i].addEventListener('click', 9000);
}
addEventListener is a function that wants a function as the 2nd argument, not a number. so nothing would happen.
As you are said the function returns nothing. in fact it is not a function at all. The idea of closure is to return function which uses values in outer scope.
var submitSOBox = document.getElementsByClassName("submitSOBox");
for (i = 0; i < submitSOBox.length; i++) {
//changes are possible here but we'll do it later
submitSOBox[i].addEventListener('click', SOBoxColor(i));
//in your initial code SOBoxColor(i) returned undefined (no return)
//and event listener must be a function. Yours was not.
}
//change your function like this:
function SOBoxColor(i) {
//get the element from collection.
//both collection and i are available at this point
var elem = submitSOBox[i];
return function(){
//function knows elem from parent scope
elem.style.backgroundColor = "blue";
};
}
I hope this explanations are helpful.
Extra
This is an exercise with closure. If it were not then it would be simpler solution:
var submitSOBox = document.getElementsByClassName("submitSOBox");
for (i = 0; i < submitSOBox.length; i++) {
submitSOBox[i].addEventListener('click', function(){
this.style.backgroundColor = "blue";
//in this context **this** is clicked element
});
}
Basically
submitSOBox[i].addEventListener('click', SOBoxColor(i));
He is excecuting the function SOBoxColor with the current value of i as argument.
Also when when you add an event listener to an element, you have already the implicit value of this pointing at the DOM element. So just the change the code:
function SOBoxColor() {
this.style.backgroundColor = "blue";
}
var submitSOBox = document.getElementsByClassName("submitSOBox");
for (i = 0; i < submitSOBox.length; i++) {
submitSOBox[i].addEventListener('click', SOBoxColor);//your passing the function
}
Also it's all more simple very simple with jQuery:
$(".submitSOBox").click(function(){ $(this).css("background-color","blue"); });

why javascript use call by reference , sometimes :D [duplicate]

This code is supposed to pop up an alert with the number of the image when you click it:
for(var i=0; i<10; i++) {
$("#img" + i).click(
function () { alert(i); }
);
}
You can see it not working at http://jsfiddle.net/upFaJ/. I know that this is because all of the click-handler closures are referring to the same object i, so every single handler pops up "10" when it's triggered.
However, when I do this, it works fine:
for(var i=0; i<10; i++) {
(function (i2) {
$("#img" + i2).click(
function () { alert(i2); }
);
})(i);
}
You can see it working at http://jsfiddle.net/v4sSD/.
Why does it work? There's still only one i object in memory, right? Objects are always passed by reference, not copied, so the self-executing function call should make no difference. The output of the two code snippets should be identical. So why is the i object being copied 10 times? Why does it work?
I think it's interesting that this version doesn't work:
for(var i=0; i<10; i++) {
(function () {
$("#img" + i).click(
function () { alert(i); }
);
})();
}
It seems that the passing of the object as a function parameter makes all the difference.
EDIT: OK, so the previous example can be explained by primitives (i) being passed by value to the function call. But what about this example, which uses real objects?
for(var i=0; i<5; i++) {
var toggler = $("<img/>", { "src": "http://www.famfamfam.com/lab/icons/silk/icons/cross.png" });
toggler.click(function () { toggler.attr("src", "http://www.famfamfam.com/lab/icons/silk/icons/tick.png"); });
$("#container").append(toggler);
}
Not working: http://jsfiddle.net/Zpwku/
for(var i=0; i<5; i++) {
var toggler = $("<img/>", { "src": "http://www.famfamfam.com/lab/icons/silk/icons/cross.png" });
(function (t) {
t.click(function () { t.attr("src", "http://www.famfamfam.com/lab/icons/silk/icons/tick.png"); });
$("#container").append(t);
})(toggler);
}
Working: http://jsfiddle.net/YLSn6/
Most of the answers are correct in that passing an object as a function parameter breaks a closure and thus allow us to assign things to functions from within a loop. But I'd like to point out why this is the case, and it's not just a special case for closures.
You see, the way javascript passes parameters to functions is a bit different form other languages. Firstly, it seems to have two ways of doing it depending on weather it's a primitive value or an object. For primitive values it seems to pass by value and for objects it seems to pass by reference.
How javascript passes function arguments
Actually, the real explanation of what javascript does explains both situations, as well as why it breaks closures, using just a single mechanism.
What javascript does is actually it passes parameters by copy of reference. That is to say, it creates another reference to the parameter and passes that new reference into the function.
Pass by value?
Assume that all variables in javascript are references. In other languages, when we say a variable is a reference, we expect it to behave like this:
var i = 1;
function increment (n) { n = n+1 };
increment(i); // we would expect i to be 2 if i is a reference
But in javascript, it's not the case:
console.log(i); // i is still 1
That's a classic pass by value isn't it?
Pass by reference?
But wait, for objects it's a different story:
var o = {a:1,b:2}
function foo (x) {
x.c = 3;
}
foo(o);
If parameters were passed by value we'd expect the o object to be unchanged but:
console.log(o); // outputs {a:1,b:2,c:3}
That's classic pass by reference there. So we have two behaviors depending on weather we're passing a primitive type or an object.
Wait, what?
But wait a second, check this out:
var o = {a:1,b:2,c:3}
function bar (x) {
x = {a:2,b:4,c:6}
}
bar(o);
Now see what happens:
console.log(o); // outputs {a:1,b:2,c:3}
What! That's not passing by reference! The values are unchanged!
Which is why I call it pass by copy of reference. If we think about it this way, everything makes sense. We don't need to think of primitives as having special behavior when passed into a function because objects behave the same way. If we try to modify the object the variable points to then it works like pass by reference but if we try to modify the reference itself then it works like pass by value.
This also explains why closures are broken by passing a variable as a function parameter. Because the function call will create another reference that is not bound by the closure like the original variable.
Epilogue: I lied
One more thing before we end this. I said before that this unifies the behavior of primitive types and objects. Actually no, primitive types are still different:
var i = 1;
function bat (n) { n.hello = 'world' };
bat(i);
console.log(i.hello); // undefined, i is unchanged
I give up. There's no making sense of this. It's just the way it is.
It's because you are calling a function, passing it a value.
for (var i = 0; i < 10; i++) {
alert(i);
}
You expect this to alert different values, right? Because you are passing the current value of i to alert.
function attachClick(val) {
$("#img" + val).click(
function () { alert(val); }
);
}
With this function, you'd expect it to alert whatever val was passed into it, right? That also works when calling it in a loop:
for (var i = 0; i < 10; i++) {
attachClick(i);
}
This:
for (var i = 0; i < 10; i++) {
(function (val) {
$("#img" + val).click(
function () { alert(val); }
);
})(i);
}
is just an inline declaration of the above. You are declaring an anonymous function with the same characteristics as attachClick above and you call it immediately. The act of passing a value through a function parameter breaks any references to the i variable.
upvoted deceze's answer, but thought I'd try a simpler explanation. The reason the closure works is that variables in javascript are function scoped. The closure creates a new scope, and by passing the value of i in as a parameter, you are defining a local variable i in the new scope. without the closure, all of the click handlers you define are in the same scope, using the same i. the reason that your last code snippet doesn't work is because there is no local i, so all click handlers are looking to the nearest parent context with i defined.
I think the other thing that might be confusing you is this comment
Objects are always passed by reference, not copied, so the self-executing function call should make no difference.
this is true for objects, but not primitive values (numbers, for example). This is why a new local i can be defined. To demonstrate, if you did something weird like wrapping the value of i in an array, the closure would not work, because arrays are passed by reference.
// doesn't work
for(var i=[0]; i[0]<10; i[0]++) {
(function (i2) {
$("#img" + i2[0]).click(
function () { alert(i2[0]); }
);
})(i);
}
In the first example, there is only one value of i and it's the one used in the for loop. This, all event handlers will show the value of i when the for loop ends, not the desired value.
In the second example, the value of i at the time the event handler is installed is copied to the i2 function argument and there is a separate copy of that for each invocation of the function and thus for each event handler.
So, this:
(function (i2) {
$("#img" + i2).click(
function () { alert(i2); }
);
})(i);
Creates a new variable i2 that has it's own value for each separate invocation of the function. Because of closures in javascript, each separate copy of i2 is preserved for each separate event handler - thus solving your problem.
In the third example, no new copy of i is made (they all refer to the same i from the for loop) so it works the same as the first example.
Code 1 and Code 3 didn't work because i is a variable and values are changed in each loop. At the end of loop 10 will be assigned to i.
For more clear, take a look at this example,
for(var i=0; i<10; i++) {
}
alert(i)
http://jsfiddle.net/muthkum/t4Ur5/
You can see I put a alert after the loop and it will show show alert box with value 10.
This is what happening to Code 1 and Code 3.
Run the next example:
for(var i=0; i<10; i++) {
$("#img" + i).click(
function () { alert(i); }
);
}
i++;
You'll see that now, 11 is being alerted.
Therefore, you need to avoid the reference to i, by sending it as a function parameter, by it's value. You have already found the solution.
One thing that the other answers didn't mention is why this example that I gave in the question doesn't work:
for(var i=0; i<5; i++) {
var toggler = $("<img/>", { "src": "http://www.famfamfam.com/lab/icons/silk/icons/cross.png" });
toggler.click(function () { toggler.attr("src", "http://www.famfamfam.com/lab/icons/silk/icons/tick.png"); });
$("#container").append(toggler);
}
Coming back to the question months later with a better understanding of JavaScript, the reason it doesn't work can be understood as follows:
The var toggler declaration is hoisted to the top of the function call. All references to toggler are to the same actual identifier.
The closure referenced in the anonymous function is the same (not a shallow copy) of the one containing toggler, which is being updated for each iteration of the loop.
#2 is quite surprising. This alerts "5" for example:
var o;
setTimeout(function () { o = {value: 5}; }, 100);
setTimeout(function () { alert(o.value) }, 1000);

Explanation needed : Cannot understand javascript returning a function

I am more of a C++/Java programmer and fairly new to Javascript. I have been reading Javascript the good parts book.In the chapter on Functions, the author cites and example of attaching an event listener to nodes in the DOM by doing the following
// When you click on a node, an alert box will display the ordinal of the node.
1 var add_the_handlers = function (nodes) {
2 var i;
3 for (i = 0; i < nodes.length; i += 1) {
4 nodes[i].onclick = function (i) {
5 return function (e) {
6 alert(e);
7 };
8 }(i);
9
10 };
The aim of the above function is to add an onclick event handler to all of the nodes in the DOM such that when anyone of the nodes is clicked it alerts the number assigned to it or its ordinal.
I am not able to understand lines 5 through 8. I understand that the function (the onclick = function(i)...) is immediately called with i as its argument. But how is this 'i' being passed to the function that is being returned ? How is 'e' getting the value of 'i' that is later being alerted?
I tried playing around with it and ran the following code in my browser
window.onclick = function(i){
return function(e){
console.log("Inner function called");
alert(e);
}
}(4);
I expected an alert window with the number 4 in it. Instead the alert window shows [object MouseEvent].
I would really appreciate if someone explained this to me.
Thanks
It's a mistake in the book. They were trying to demonstrate the awful (performance-wise) pattern of using an otherwise unnecessary function scope to allow an inline function to closure around a variable in an outer scope, which, because it is not referenced anywhere else, becomes a kind of private variable referenced only by the inline function. The code would do its job if the i variable was indeed used as the argument to the alert() call:
var add_the_handlers = function(nodes) {
for (var i = 0; i < nodes.length; ++i) {
nodes[i].onclick = function(i) {
return function() {
alert(i);
};
}(i);
} // end for
};
add_the_handlers(document.getElementsByTagName('div'));
http://jsfiddle.net/pzht9mLu/
In the book's code, instead of closuring around the function parameter i in the outer scope, the inline function that becomes the onclick handler doesn't closure around anything. Instead, the e variable that is used as the argument in the alert() call binds to the function parameter e of the inline function (that's why no closuring happens; a closure only happens when a variable reference inside a function scope fails to bind to any local inside the scope, and instead binds to a variable in an outer scope). Because the function ends up serving as an event handler, its parameter e is set equal to the Event object that initiated the callback (if and when it happens), and that's why you see that in the alert message.
What you should realize is that in lines 5-9 (where's number 7 btw?:)) a new function is created, which will be bound as the event listener function of the onclick.
See line 9:
}(i);
This means that the above function is immediately invoked, passing in i as its function parameter, and then it returns a new function.
Should you, for example, use the value of i in this inner function, it is captured in a closure, so the onclick function will have access to it, even though it is invoked from an entirely different scope.
I tried playing around with it and ran the following code in my
browser
window.onclick = function(i){
return function(e){
console.log("Inner function called");
alert(e);
} }(4);
I expected an alert window with the number 4 in it. Instead the alert window shows [object MouseEvent].
The argument i is never used. The alert produces e, which is the argument to the onclick event handler. Which is a MouseEvent object.
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (i) {
return function (e) {
alert(e);
};
}(i);
};
The aim of the above function is to add an onclick event handler to
all of the nodes in the DOM such that when anyone of the nodes is
clicked it alerts the number assigned to it or its ordinal.
I am not able to understand lines 5 through 8. I understand that the
function (the onclick = function(i)...) is immediately called with i
as its argument. But how is this 'i' being passed to the function that
is being returned ? How is 'e' getting the value of 'i' that is later
being alerted?
i Isn't passed to the second function, and will never be shown by your current code. What you're doing now is effectively no different than this:
nodes[i].onclick = function (e) { alert(e); };
If you want to produce an alert with the correct value of i in it, you'd simply use your original code but alert i instead of e.

Extra parentheses on function [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What do parentheses surrounding a JavaScript object/function/class declaration mean?
What does this “(function(){});”, a function inside brackets, mean in javascript?
A Javascript function
I encountered markup similar to this:
var something = (function(){
//do stuff
return stuff;
})()
document.ondblclick = function(e) { alert(something(e)) };
I don't understand the opening ( and closing )() in the something variable.
Could you explain the difference to writing it like this?
var something = function(){
//do stuff
return stuff;
};
Thanks!
It's probably easier to understand if you leave the redundant parens out because they serve no purpose:
var something = function() {
return 3;
} // <-- a function.
(); // now invoke it and the result is 3 (because the return value is 3) assigned to variable called something
console.log(something) //3 because the function returned 3
var something = function() {
return 3;
}; // a function is assigned to a variable called something
console.log(something) //logs the function body because it was assigned to a function
console.log(something()) //invoke the function assigned to something, resulting in 3 being logged to the console because that's what the function returns
(function(){ ... }) is a (anonymous) function expression, you could e.g. assign that value to a variable.
The brackets behind it will immidiately execute the function expression, resulting in the return value of the function (in here: stuff). The construct is called IIFE.
When stuff is a function (which I assume, because you invoke something lateron), this is called a closure - the returned function (stuff, assigned to something) still has access to the variables in the execution context of that anonymous function.
On the question what it does, read all the comments and other answers. They are absolutely right.
Why would you want to use it? You find this pattern very often when using closures. The intent of the following code snippet is to add an event handler to 10 different DOM elements and each one should alert it’s ID attribute (e.g. “You’ve clicked 3″). You should know that if this was your actual intent, then there is a much easier way to do this, but for academic reasons let’s stick with this implementation.
var unorderedList = $( "ul" );
for (var i = 0; i < 10; i++) {
$("<li />", {
id: i,
text: "Link " + i,
click: function() {
console.log("You've clicked " + i);
}
}).appendTo( unorderedList );
}
The output of the above code may not be what you first expect. The result of every click handler will be “You’ve clicked 9″ because the value of i at the point the event handler was fired is “9″. What the developer really wanted is for the value of i to be displayed at the point in time the event handler was defined.
In order to fix the above bug we can introduce a closure.
var unorderedList = $( "ul" ), i;
for (i = 0; i < 10; i++) {
$("<li />", {
id: i,
text: "Link " + i,
click: function(index) {
return function() {
console.log("You've clicked " + index);
}
}(i)
}).appendTo( unorderedList );
}
You can execute and modify the above code from jsFiddle.
One way to fix the above code is to utilize a self-executing anonymous function. That is a fancy term that means we are going to create a nameless function and then immediately call it. The value of this technique is that the scope of the variable stays within the function. So, first we will surround the event handler content in a function and then immediately call the function and pass in the value of i. By doing that, when the event handler is triggered it will contain the value of i that existed when the event handler was defined.
Further reading on closures: Use Cases for JavaScript Closures
All of the answers were good, but I think the simplest answer has been skimmed over:
var something = (function(){
//do stuff
return stuff;
})()
After this code executes, something becomes stuff. The function that returned stuff is executed before something is assigned.
var something = function(){
//do stuff
return stuff;
};
After this code executes, something is a function which returns stuff. The function that returns stuff was never executed.
Check the JavaScript FAQ section, too: Here are some pretty good explanations and examples
Ok, why should you use this:
Suppose my script is running, and there are a couple of things (I'm, for instance, looping through a nodes list) I might be needing later on. That's why I might choose to do something like this:
for(var i=0;i<nodesList.lenght;i++)
{
if (nodesList[i].id==="theOneINeed")
{
aClosure = (function(node,indexInNodesList)//assign here
{
return function()
{
node.style.display = 'none';//usable after the parent function returns
alert(indexInNodesList+ ' is now invisible');
}
})(nodesList[i],i);//pass the element and its index as arguments here
break;
}
}
i = 99999;
aClosure();//no arguments, but it'll still work, and say i was 15, even though I've just
//assigned another value to i, it'll alert '15 is now invisible'
What this enables me to do is to prevent function arguments from being garbage collected. Normally, after a function returns, all its var's and arguments are GC'd. But in this case, the function returned another function that has a link to those arguments (it needs them), so they're not GC'ed for as long as aClosure exists.
As I said in my comment. Google closures, practice a bit, and it'll dawn on you... they really are quite powerful

Closures: Line by Line explanation of "Javascript: Good Parts" example?

I'm reading "Javascript: The Good Parts" and am totally baffled by what's really going on here. A more detailed and/or simplified explanation would be greatly appreciated.
// BAD EXAMPLE
// Make a function that assigns event handler functions to an array of nodes the wrong way.
// When you click on a node, an alert box is supposed to display the ordinal of the node.
// But it always displays the number of nodes instead.
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (e) {
alert(i);
}
}
};
// END BAD EXAMPLE
The add_the_handlers function was intended to give each handler a unique number (i). It fails because the handler functions are bound to the variable i, not the value of the variable i at the time the function was made:
// BETTER EXAMPLE
// Make a function that assigns event handler functions to an array of nodes the right way.
// When you click on a node, an alert box will display the ordinal of the node.
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (i) {
return function (e) {
alert(i);
};
}(i);
}
};
Now, instead of assigning a function to onclick, we define a function and immediately invoke it, passing in i. That function will return an event handler function that is bound to the value of i that was passed in, not to the i defined in add_the_handlers. That returned function is assigned to onclick.
I think this is a very common source of confusion for newcomers to JavaScript. First I would suggest checking out the following Mozilla Dev article for brief introduction on the topic of closures and lexical scoping:
Mozilla Dev Center: Working with Closures
Let's start with the bad one:
var add_the_handlers = function (nodes) {
// Variable i is declared in the local scope of the add_the_handlers()
// function.
var i;
// Nothing special here. A normal for loop.
for (i = 0; i < nodes.length; i += 1) {
// Now we are going to assign an anonymous function to the onclick property.
nodes[i].onclick = function (e) {
// The problem here is that this anonymous function has become a closure. It
// will be sharing the same local variable environment as the add_the_handlers()
// function. Therefore when the callback is called, the i variable will contain
// the last value it had when add_the_handlers() last returned.
alert(i);
}
}
// The for loop ends, and i === nodes.length. The add_the_handlers() maintains
// the value of i even after it returns. This is why when the callback
// function is invoked, it will always alert the value of nodes.length.
};
We can tackle this problem with more closures, as Crockford suggested in the "good example". A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. In JavaScript, the environment of the closure consists of any local variables that were in-scope at the time that the closure was created:
// Now we are creating an anonymous closure that creates its own local
// environment. I renamed the parameter variable x to make it more clear.
nodes[i].onclick = function (x) {
// Variable x will be initialized when this function is called.
// Return the event callback function.
return function (e) {
// We use the local variable from the closure environment, and not the
// one held in the scope of the outer function add_the_handlers().
alert(x);
};
}(i); // We invoke the function immediately to initialize its internal
// environment that will be captured in the closure, and to receive
// the callback function which we need to assign to the onclick.
Rather than having the callbacks all sharing a single environment, the closure function creates a new environment for each one. We could also have used a function factory to create a closure, as in the following example:
function makeOnClickCallback (x) {
return function (e) {
alert(x);
};
}
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = makeOnClickCallback(i);
}
It's all about closures. In the first example, "i" will be equal to "nodes.length" for every click event handler, because it uses "i" from the loop which creates the event handlers. By the time the event handler is called, the loop will have ended, so "i" will be equal to "nodes.length".
In the second example, "i" is a parameter (so a local variable). The event handlers will use the value of the local variable "i" (the parameter).
In both examples any node that's passed has an onclick event handler bound to it (just like <img src="..." onclick="myhandler()"/>, which is bad practice after all).
The difference is that in the bad example every closure (the event handler functions, that is) is referencing the exact same i variable due to their common parent scope.
The good example makes use of an anonymous function that gets executed right away. This anonymous function references the exact same i variable as in the bad example BUT since it is executed and provided with i as its first parameter, i's value is assigned to a local variable called ... eh? ... i, exactely - thus overwriting the one defined in the parent's scope.
Let's rewrite the good example to make it all clear:
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (newvar) {
return function (e) {
alert(nevar);
};
}(i);
}
};
Here we replaced i in the returned event handler function with newvar and it still works, because newvar is just what you'd expect - a new variable inherited from the anonymous function's scope.
Good luck figuring it out.
It has to do with closure.
When you do the thing in the bad example,
when you click each node, you will get the latest i value (i.e. you have 3 nodes, no matter what node you click you will get 2). since your alert(i) is bound to a reference of variable i and not the value of i at the moment it was bound in the event handler.
Doing it the better example way, you bound it to what i as at the moment that it was iterated on, so clicking on node 1 will give you 0, node 2 will give you 1 and node 3 will give you 2.
basically, you are evaluating what i is immediately when it is called at the line }(i) and it got passed to parameter e which now hold the value of what i is at that moment in time.
Btw... I think there is a typo there in the better example part... it should be alert(e) instead of alert(i).

Categories

Resources