0

Consider following code:

    var a = [ { a: 'I' }, { a: 'II' }, { a: 'III' } ];

    for( var i=0; i<a.length; i++ ){

            var j = i;
            var x = a[ i ];

            x.bb = function(){
                    x.b = "-" + x.a + "-";
            }
            x.cc = function(){
                    a[ i ].c = "-" + a[ i ].a + "-";
            }
            x.dd = function(){
                    a[ j ].d = "-" + a[ j ].a + "-";
            }
    }

    for( var i=0; i<a.length; i++ ){
            a[i].bb();
            a[i].cc();
            a[i].dd();
    }

    console.log( a );

will result in (functions left out):

[ { a: 'I', c: '-I-' },
  { a: 'II', c: '-II-' },
  { a: 'III', b: '-III-', d: '-III-', c: '-III-' } ]

So only cc() does what I intended to do.

My question now is:

  1. What are the rules here how the variables are stored in the closure. (Obviously i is stored as a constant while j and x are stored as reference to the local variables which are changed while the for-loop iterates. But why is that?) There was my mistake. The code did only work because the second and the first i where the same!
  2. Is there a way to write bb() in a way that it stores a reference to a[ i ] so that I don't have to use the index?

Thanks!

Scheintod
  • 7,953
  • 9
  • 42
  • 61
  • 3
    There are no closures here. – Niet the Dark Absol Dec 08 '14 at 14:18
  • Sorry for the wrong wording then. How would you name these functions which store references to surrounding scope then? – Scheintod Dec 08 '14 at 14:21
  • 1
    Closures work by holding a reference to an outer variable environment. In this case, all of your functions have the same outer variable environment (i.e., global scope). So, when you refer to `i` and `j` inside the function, you're referring to the global `i` and `j` that exist outside the function. If you want to capture a *value*, rather than a reference to a (potentially changing) variable, see [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – apsillers Dec 08 '14 at 14:21

1 Answers1

2

You need to keep in mind that JavaScript does not have block scoping (well, not yet anyway, ES6 is working that in with let), instead, everything is at the "function" level. That is, all variables are scoped to their most recent function definition.

Variables are declared using the var statement, and according to your code, every variable you have is scoped to the outer container for this code. In other words, if this code is in a Node module, then all of the variables in here are scoped to the entire module. As such, although your functions defined inside the for loop are closures (because all JS functions are closures), they are still just using the variables defined within the module at large since there is no need for JS to look any further.

To illustrate this answer to question 1, consider the following:

var o = {};
var x = 1;

function foo() {
    var x = 2;
    o.bar = function() {
        console.log(x);
    }
}

o.bar(); // will output "2" because of the closure created in `foo()`

As for your second question, because a[i] is an object, any variable you assign that object to (in this case, x) will be a reference, and all references will point to the same object in memory. I'm not what you're trying to accomplish with bb(), but you are "storing a reference to a[ i ]" by using x. Perhaps you have a more specific example?

Jordan Kasper
  • 13,153
  • 3
  • 36
  • 55
  • A-h. What really did confuse me is that it seemed to work with `i`. But now I notice that my first `i` and my second one are the same (I would expect some sane interpreter to complain over the second `var`) and using another variable in the second loop leads to failure. – Scheintod Dec 08 '14 at 14:35
  • @Scheintod Just FYI, duplicate `var` statements are explicitly allowed (or permissively ignored, anyway) in the ES5 specification, [section 10.5, step 8](http://www.ecma-international.org/ecma-262/5.1/#sec-10.5). – apsillers Dec 08 '14 at 15:16
  • @apsillers makes a good point, which is why you should **always use [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)** in your JavaScript, which will force the interpreter to make duplicate `var` statements an error (as it should be)! It's easy, just place `"use strict;"` at the top of your functions/modules. – Jordan Kasper Dec 08 '14 at 15:49