Lexical scope and looping

Discussion in 'Javascript' started by enaeher@gmail.com, Apr 5, 2007.

  1. Guest

    I would expect this code:

    globalFnArray = [];
    for (var i = 0; i < 5; i++) { globalFnArray.push (function () { alert
    (i) }); }
    for (var j = 0; j < 5; j++) { globalFnArray[j](); }

    to alert 0, 1, 2, 3, and 4.

    Instead, I get 5, 5, 5, 5, and 5 in Firefox, indicating that all of
    the functions in globalFnArray are closing over the final value of i
    (after the for loop's conditional (i < 5) has failed and the loop has
    exited), rather than the value to which i was bound during the
    iteration of the loop when the function was defined. This does not
    seem consistent with lexical scope as I understand it. Is the for loop
    a special case? I'd appreciate any suggestions as to what I'm missing.

    Thanks,
    --Eli
    , Apr 5, 2007
    #1
    1. Advertising

  2. -Lost Guest

    <> wrote in message
    news:...
    >I would expect this code:
    >
    > globalFnArray = [];
    > for (var i = 0; i < 5; i++) { globalFnArray.push (function () { alert
    > (i) }); }
    > for (var j = 0; j < 5; j++) { globalFnArray[j](); }
    >
    > to alert 0, 1, 2, 3, and 4.
    >
    > Instead, I get 5, 5, 5, 5, and 5 in Firefox, indicating that all of
    > the functions in globalFnArray are closing over the final value of i
    > (after the for loop's conditional (i < 5) has failed and the loop has
    > exited), rather than the value to which i was bound during the
    > iteration of the loop when the function was defined. This does not
    > seem consistent with lexical scope as I understand it. Is the for loop
    > a special case? I'd appreciate any suggestions as to what I'm missing.


    I am not entirely sure how this relates to scope.

    The problem is that you are storing functions with a variable inside them. Let me just
    illustrate. What is being stored in each call to push() is:

    function () {
    alert(i);
    }

    So, when you finally call the function, i has already reached 5, and of course stays
    there.

    So each call iteration through globalFnArray will result in alert('5'); The only way I
    see of you achieving what you want is:

    globalFnArray = [];
    for (var i = 0; i < 5; i++)
    {
    globalFnArray.push(function()
    {
    alert(i);
    });
    }
    for (var i = 0, j = 0; j < 5; i++, j++)
    {
    globalFnArray[j](i);
    }

    *OR* change just these lines:

    globalFnArray.push(function() to globalFnArray.push(function(i)

    Then you could globalFnArray[j](i) anywhere you wanted, as long as it is fed a variable of
    i.

    -Lost
    -Lost, Apr 5, 2007
    #2
    1. Advertising

  3. -Lost Guest

    "-Lost" <> wrote in message
    news:...
    > <> wrote in message
    > news:...
    >>I would expect this code:
    >>
    >> globalFnArray = [];
    >> for (var i = 0; i < 5; i++) { globalFnArray.push (function () { alert
    >> (i) }); }
    >> for (var j = 0; j < 5; j++) { globalFnArray[j](); }
    >>
    >> to alert 0, 1, 2, 3, and 4.
    >>
    >> Instead, I get 5, 5, 5, 5, and 5 in Firefox, indicating that all of
    >> the functions in globalFnArray are closing over the final value of i
    >> (after the for loop's conditional (i < 5) has failed and the loop has
    >> exited), rather than the value to which i was bound during the
    >> iteration of the loop when the function was defined. This does not
    >> seem consistent with lexical scope as I understand it. Is the for loop
    >> a special case? I'd appreciate any suggestions as to what I'm missing.

    >
    > I am not entirely sure how this relates to scope.
    >
    > The problem is that you are storing functions with a variable inside them. Let me just
    > illustrate. What is being stored in each call to push() is:
    >
    > function () {
    > alert(i);
    > }
    >
    > So, when you finally call the function, i has already reached 5, and of course stays
    > there.
    >
    > So each call iteration through globalFnArray will result in alert('5'); The only way I
    > see of you achieving what you want is:
    >
    > globalFnArray = [];
    > for (var i = 0; i < 5; i++)
    > {
    > globalFnArray.push(function()
    > {
    > alert(i);
    > });
    > }
    > for (var i = 0, j = 0; j < 5; i++, j++)
    > {
    > globalFnArray[j](i);


    Oops! Here I meant:

    globalFnArray[j]();

    > }
    >
    > *OR* change just these lines:
    >
    > globalFnArray.push(function() to globalFnArray.push(function(i)
    >
    > Then you could globalFnArray[j](i) anywhere you wanted, as long as it is fed a variable
    > of i.
    -Lost, Apr 5, 2007
    #3
  4. Guest

    > The problem is that you are storing functions with a variable inside them.

    Yes, I am trying to make a closure.

    However, after further experimentation I've realized that I did
    misunderstand lexical scope -- I thought that functions could close
    over a variable value at the point in a lexical block (such as a
    function definition) at which they were defined, but in fact they can
    only close over the last value in that lexical block, irrespective of
    where in the block the function is defined.

    So I think the way to do what I was trying to do is:

    globalFnArray = [];

    for (var i = 0; i < 5; i++) {
    globalFnArray.push (
    function () {
    var x = i;
    return function () { alert (x) }
    }()
    );
    }

    for (var j = 0; j < 5; j++) { globalFnArray[j](); }

    If there is a nicer way to do this which doesn't require using a
    throwaway variable, I'd love to know.

    Thanks,

    --Eli
    , Apr 5, 2007
    #4
  5. Kevin Guest

    On Apr 4, 8:45 pm, wrote:
    > If there is a nicer way to do this which doesn't require using a
    > throwaway variable, I'd love to know.


    You could use a constructor to create the function type for what
    you're trying to do:

    globalFnArray = [];

    function alertfunc(x) {
    return function() { alert(x); };
    }

    for (var i = 0; i < 5; i++) {
    globalFnArray.push ( new alertfunc(i) );
    }

    for (var j = 0; j < 5; j++) { globalFnArray[j](); }


    Still not the prettiest thing, but maybe more like what you're looking
    for?

    - Kevin
    Kevin, Apr 6, 2007
    #5
  6. RobG Guest

    On Apr 5, 10:45 am, wrote:
    > > The problem is that you are storing functions with a variable inside them.

    >
    > Yes, I am trying to make a closure.


    You did, but to a global variable (i). In this case, you can set i to
    whatever value you want later, all the functions will show the same
    value.

    >
    > However, after further experimentation I've realized that I did
    > misunderstand lexical scope -- I thought that functions could close
    > over a variable value at the point in a lexical block (such as a
    > function definition) at which they were defined, but in fact they can
    > only close over the last value in that lexical block, irrespective of
    > where in the block the function is defined.


    "close over"? All the function objects that you put in the array have
    a reference back to the same i variable, which is global. Consider:

    var gArray = [];

    function foo(){

    // This i is local to foo
    for (var i=0; i<5; i++){

    // Each fn has a closure back to foo's i
    gArray.push( function(){alert(i);} );
    }
    }

    foo();

    // This i is global
    for (var i=0, len=gArray.length; i<len; i++){
    gArray();
    }


    >
    > So I think the way to do what I was trying to do is:
    >
    > globalFnArray = [];
    >
    > for (var i = 0; i < 5; i++) {
    > globalFnArray.push (
    > function () {
    > var x = i;
    > return function () { alert (x) }
    > }()
    > );
    >
    > }
    >
    > for (var j = 0; j < 5; j++) { globalFnArray[j](); }
    >
    > If there is a nicer way to do this which doesn't require using a
    > throwaway variable, I'd love to know.


    You aren't throwing away the variable at all, you are putting it into
    the new function object with the value of whatever i was when you
    create the function.

    Anyhow, you have to break the closure somehow. You can also use a new
    Function, however I think the above way is better as its much more
    efficient - new Function is similar to using eval. But for the
    record:

    for (var i=0; i<5; i++){
    gArray.push( new Function('alert(' + i + ');') );
    }


    --
    Rob
    RobG, Apr 6, 2007
    #6
  7. Guest

    On Apr 4, 5:45 pm, wrote:
    > > The problem is that you are storing functions with a variable inside them.

    >
    > Yes, I am trying to make a closure.
    >
    > However, after further experimentation I've realized that I did
    > misunderstand lexical scope -- I thought that functions could close
    > over a variable value at the point in a lexical block (such as a
    > function definition) at which they were defined, but in fact they can
    > only close over the last value in that lexical block, irrespective of
    > where in the block the function is defined.
    >
    > So I think the way to do what I was trying to do is:
    >
    > globalFnArray = [];
    >
    > for (var i = 0; i < 5; i++) {
    > globalFnArray.push (
    > function () {
    > var x = i;
    > return function () { alert (x) }
    > }()
    > );
    >
    > }
    >
    > for (var j = 0; j < 5; j++) { globalFnArray[j](); }
    >
    > If there is a nicer way to do this which doesn't require using a
    > throwaway variable, I'd love to know.
    >


    for (var i = 0; i < 5; i++) {
    globalFnArray.push (
    function (myPosn) {
    return function () { alert (myPosn); }
    }(i)
    );
    }
    --
    ../rh
    , Apr 6, 2007
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Matt Knepley

    Lexical Scope

    Matt Knepley, Oct 30, 2003, in forum: Python
    Replies:
    3
    Views:
    414
    Paul Clinch
    Oct 30, 2003
  2. Replies:
    18
    Views:
    489
    Bengt Richter
    Dec 17, 2005
  3. globalrev

    python: lexical or dynamic scope?

    globalrev, May 13, 2008, in forum: Python
    Replies:
    3
    Views:
    869
    Mark Wooding
    May 14, 2008
  4. Xah Lee
    Replies:
    0
    Views:
    2,222
    Xah Lee
    Feb 26, 2009
  5. bernd
    Replies:
    2
    Views:
    630
    bernd
    Jan 26, 2012
Loading...

Share This Page