1

For tl;dr, When creating click handlers for rows in a table, using a closure makes the function point to the same place in memory, not a different place each time like with the code below.

otherwise:

I'm learning javascript and I think I understand what a closure is and why it's useful. Here's my reasoning; is it correct?

For an HTML table, the code below always shows the last row being clicked, even if I click on the first or second row. My reasoning is the code creates 3 different stack frames, one each for i equaling 0, 1 and 2. Since 2 is the most recent stack frame, the return value of fnPtr points to 2.

var table = document.getElementById("tableid3");
var rows = table.getElementsByTagName("tr");
for (var i = 0; i < rows.length; i++) {
    var curRow = table.rows[i];
    //get cell data from first col of row
    var cell = curRow.getElementsByTagName("td")[0]; 
    var fnPtr = function() {
        alert("row " + i + " data="+ cell.innerHTML);
    }
curRow.onclick = fnPtr;
}

Now the code below (from a SO question) uses a closure and creates only a single stack frame, the frame for createfunc(). But inside this frame is a local variable tmp, which points to the anonymous function. When createfunc() is called with 0, 1, and 2, the return value points to the same stack frame (the stack frame inside the stack frame of createfunc()) meaning that 3 stack frames are not created. When createfunc() returns each time, the value in the return value slot points to the same place in memory, not a different place each time like with the code above.

function createfunc(i) {
    var tmp = function() { console.log("My value: " + i); };
    console.log(tmp);
    return tmp;
}
for (var i = 0; i < 3; i++) {
    funcs[i] = createfunc(i);
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

Am I understanding how closures work?

Clara
  • 281
  • 1
  • 3
  • 13
  • `var curRow = table.rows[i];` should be `var curRow = rows[i]`. – Travis J Dec 26 '12 at 02:07
  • 3
    Technically, every function is a closure in JS because every function has access to variables defined in a higher scope. `fnPtr` in the first snippet is a closure because it closes over `i` and `cell`. In your second example, `tmp` is a closure because it closes over `i`. The "trick" to solve the loop problem is to create a new scope which can only be achieved by *calling* a function. And in your second example, `createfunc` always returns a new function and each of them is of course placed at a different location in memory. – Felix Kling Dec 26 '12 at 02:07
  • 1
    See the following answer to understand closures in JavaScript: http://stackoverflow.com/a/12931295/783743 – Aadit M Shah Dec 26 '12 at 02:17
  • 1
    To make it short: It all depends on **when** you read the value of a variable. In the first case it's when the event handlers are called, which is a long time after the `for` loop terminated. In the second case you read `i` inside the loop by passing it to another function. – Felix Kling Dec 26 '12 at 02:23

2 Answers2

1

This is about scope, not the stack. Every function definition creates a new scope in JavaScript, and functions always have access to their ancestor scopes.

So on your first example, all instances of fnPtr have access to the same i from the parent scope. By the time any of the rows is clicked, the for loop is already finished, and i will be 3.

On the second example, each returned tmp function will have access to the i argument passed to the factory function, which will be different on each iteration/call.

bfavaretto
  • 71,580
  • 16
  • 111
  • 150
0

I'm not sure its a good idea to think of the way javascript works in terms of stack frames - javascript is just too high level for that.

In the first code snippet, the fnPtr function object you create captures the actual variable(but not yet the value of it), and so if that variable later changes, when the function is called, the i variables value at the current time is used. Your loop may have created 100 fnPtr function objects, but each and every one of them holds a reference to the exact same i variable.

The second snippet exploits the fact that when you pass a primitive value(eg, a number) to a function as a function argument, javascript creates a copy of it. So, each time you call createfunc, a new copy of the variable is made, and the value of this new variable is set to whatever value the argument had at the moment that createfunc was called. My emphasis is on the fact that this is a new variable, and no other function hold references to it. So when your createfunc returns a new function, that new function holds a reference to that new exclusive i variable that was copied.

goat
  • 31,486
  • 7
  • 73
  • 96