views:

48

answers:

3

In Javascript, I am sometimes too immerged in the idea that a function creates a new scope, that sometimes I even think the following anonymous function will create a new scope when it is being defined and assigned to onclick:

<a href="#" id="link1">ha link 1</a>
<a href="#" id="link2">ha link 2</a>
<a href="#" id="link3">ha link 3</a>
<a href="#" id="link4">ha link 4</a>
<a href="#" id="link5">ha link 5</a>


<script type="text/javascript">

    for (i = 1; i <= 5; i++) {

        document.getElementById('link' + i).onclick = function() { var x = i; alert(x); return false; }
    }

</script>

but in fact, the anonymous function will create a new scope, that's right, but ONLY when it is being invoked, is that so? So the x inside the anonymous function is not created, no new scope is created. When the function was later invoked, there is a new scope alright, but the i is in the outside scope, and the x gets its value, and it is all 6 anyways.

The following code will actually invoke a function and create a new scope and that's why the x is a new local variable x in the brand new scope each time, and the invocation of the function when the link is clicked on will use the different x in the different scopes.

<a href="#" id="link1">ha link 1</a>
<a href="#" id="link2">ha link 2</a>
<a href="#" id="link3">ha link 3</a>
<a href="#" id="link4">ha link 4</a>
<a href="#" id="link5">ha link 5</a>


<script type="text/javascript">

    for (i = 1; i <= 5; i++) {

        (function() {
            var x = i;
            document.getElementById('link' + i).onclick = function() { alert(x); return false; }
        })();  // invoking it now!
    }

</script>

If we take away the var in front of x, then it is a global x and so no local variable x is created in the new scope, and therefore, clicking on the links get all the same number, which is the value of the global x.

Update: the question is: we have to be careful when we analyze code, that a function will not create a scope when it is merely defined and assigned. It has to invoked. Is that so?

A: 

I do not understand your question, but I do not see anything weird or wrong with that.

In the first case, a closure is formed, and when the event is invoked, it creates its own scope and fetches the value from i in its outer scope, maintained due to the closure. But that code has already been executed so i = 6 because the loop is long finished, and thus, x = 6.

In the latter example, its the same thing, you are creating a closure, and when the event gets invoked it will fetch x from its outer scope, maintained by the closure, but it already executed, and since it ran immediately in the loop, this time x is equal to the i at the time the function was ran (to create the closure).

Francisco Soto
+1  A: 

The Scope of a function is set when the function object is created, for example:

var fn;
// augment scope chain:
with ({foo: "bar"}) {
  fn = function () { // create function
    return foo;
  };
}​​
// restored scope chain
fn(); // "bar"

In the above example, the function is created inside the with block, there the current scope is being augmented, to introduce the object with the foo property to the scope chain.

In your second example, happens the same, the onclick handler function is being created inside the auto-invoking anonymous function, which itself has created a new lexical scope.

At the moment that function is auto-invoked on each loop iteration, the value of i is captured into that scope in the x variable, then the onclick handler function is created inside that scope, and it will be able to resolve it.

CMS
+2  A: 

You're right, but you're also confusing yourself. Rather than thinking of functions as just "making a new scope" understand what's really happening instead. Javascript interprets a variable by looking at a scope chain. If the variable is not present in the function's scope, it will go up to the enclosing level's scope in the case of a closure. That part happens at the time the variable is interpreted. The scope itself is created when the function is, but where you're getting confused is nothing is "captured" in the scope at that time. It's just augmenting a scope chain.

That's why putting closures in loops is such a "gotcha" in javascript programming--you are right to put in in an auto-executing anonymous function if you want to capture the value of i at the time of function declaration. People tend to think (especially coming from procedural languages) that the outer scope is frozen and all current values are passed into the new function scope, and that's not true.

Plynx
aha, nothing was frozen but just that a copy of the scope chain is preserved so that when the function is invoked, the scope chain is used again. Thus, "closing the code and context" in a box -- closure.
動靜能量