0

I read the How do JavaScript closures work? question and thought i finally understood closures.

Especially this point is very confusing right now:

Example 7

This final example shows that each call creates a separate closure for the local variables. There is not a single closure per function declaration. There is a closure for each call to a function. ..........................................

Ok, so Example 7 shows this example:

function newClosure(someNum, someRef) {
    // Local variables that end up within closure
    var num = someNum;
    var anArray = [1,2,3];
    var ref = someRef;
    return function(x) {
        num += x;
        anArray.push(num);
        document.write('num: ' + num +
            '; anArray: ' + anArray.toString() +
            '; ref.someVar: ' + ref.someVar + "<br>");
      }
}
obj = {someVar: 4};
fn1 = newClosure(4, obj);
fn2 = newClosure(5, obj);
fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4;
fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4;
obj.someVar++;
fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;
fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;

This example works for me as it should.

So I experiemented a little and i don't really understand why this code doesn't work (looks like it doesn't even create a new closure on function call)

function createCounter(startValue){

    var localCounter;

    this.setCounter = function(firstValue) {
       localCounter = firstValue; 
    }
 
    this.getValue = function(){return localCounter;};
    this.increment = function(){localCounter++;};
    this.setCounter(startValue);

    return this;
}

var Counter1 = createCounter(1);
document.write(Counter1.getValue()); //1
Counter1.increment();
var Counter1Value = Counter1.getValue(); // 2
var Counter0 =  createCounter(0); //works as it should if i put a "new createCounter(0) here, but why?
document.write("<br >Counter 1 oldValue:" + Counter1Value); //2
document.write("<br >Counter 1 currentValue:" + Counter1.getValue()); //0 (why is the localvariable overwritten?)
document.write("<br >Counter 0 currentValue:" + Counter0.getValue()); // //0

why do i need to add the "new" keyword to create a second closure, and why are both Counter1 and Counter0 using the same localCounter variable?

Community
  • 1
  • 1

1 Answers1

0

As elclanrs says, it is all about this:

When you call createCounter(1), this is unchanged, most likely remaining the global scope. Given document.write, it's safe to say the global scope is window. So, you create window.setCounter as a function that will set localCounter, a variable hidden in a closure, and window.getValue to a function that reads this hidden variable; it initialises localCounter to 1; it then returns window and assigns it to Counter1.

Then, Counter1.getValue() calls window.getValue(), which returns 1. All good. Same with increment.

However, since you are operating on the global context all the time, when you do createCounter(0), you are overwriting window.getValue (and other functions) with a new closure that now references a different hidden localCounter, this one initialised to 0. Since both Counter0 and Counter1 point at the same object (namely, window), it is not too farfetched that Counter1.getValue() and Counter0.getValue() should also return the same thing (given that they both have no side-effects).

This changes drastically with the new keyword, which is one of the several ways in JavaScript to change what this is. If you do var Counter1 = new createCounter(1) and var Counter0 = new createCounter(0), this inside the function is a new object in both cases; and Counter1 has different closures on it than Counter0.

Specifically, new createCounter(0) will do something like this pseudocode:

var oldThis = this;  // save current context
this = {};           // make a new object
createCounter(0);    // initialize the new object
this = oldThis;      // restore current context

so you can see why the values are kept distinct.

Amadan
  • 191,408
  • 23
  • 240
  • 301
  • Thank you so much, your explanation really helped me out. – Straighter Jul 24 '15 at 10:45
  • One more tiny thing: by convention, functions that are meant to be constructors (i.e. those that should make new objects and be called with `new`) are named with `TitleCase` noun phrases (thus, `Counter`). Other variables are in `camelCase` (thus, `counter0`, `counter1`, `setCounter`...). (Except for variables that are pretending to be constants, which are in `UPPER_CASE`). Conventions make the code more readable to everyone involved. – Amadan Jul 24 '15 at 10:48