daemonl

In case you were wondering...

JS105 - Closures

Let's start with an exploration:

var Constructor = function() {   
    var a = 0;   
    this.addToA = function() { a += 1; };   
    this.getA = function()  { return a; };  
  }  

  var instance = new Constructor();  

  console.log(instance.getA()); // 0  
  instance.addToA();  
  console.log(instance.getA()); // 1  

  // But where is 'a'?  
  console.log(a); // undefined  
  console.log(instance.a); // undefined  
  console.log(Constructor.a); // undefined

AhA! A Closure! There is absolutely no way to access 'a' from outside of the scope of the constructor function.

Javascript has 'functional scope' - a variable defined outside of the function is accessible inside the function, and and function inside that function, but if a variable is defined inside a function it's not accessible from outside.

The variable call 'instance.getA()', even though it is called from outside of the function, still has access to all of the variables it had access to where it was defined.

To make this possible, javascript stores all of the variables accessible to a function in memory if that function itself is accessible from outside.

Because 'a' isn't accessible from anywhere outside of the function, you can define any variable you like inside a closure without effecting any other functions. It's a very neat and organised way to do things. It's just like any other 'object oriented' programming language's classes.

var Constructor = function()  
  {  
    var a = 0;  
    this.addToA = function()  
    {  
        a += 1;  
    }  
    this.getA = function()  
    {  
        return a;  
    }  
  }  

  var i = new Constructor();  
  var j = new Constructor();  

  console.log(i.getA()); // 0  
  console.log(j.getA()); // 0  
  i.addToA();  
  console.log(i.getA()); // 1  
  console.log(j.getA()); // 0

Changes to 'a' in 'i' have no effect on 'a' in 'j'. This is because the 'scope' of the variable is defined when the function 'constructor' is called, and it is called twice (new Constructor();). A is a 'protected variable' - it can't be accessed from outside of the instance.

Oh, by the way, there's no real need to use a capital C on Constructor, it's just convention so that others can read your code. Capitals for Constructor Functions, and lower case for instances. It also means you can have both Something and something being the Constructor of the instance.

Now what if we DID want 'a' to increase in both i and j?

var a = 0;  
var Constructor = function()   
{  

  this.addToA = function()  
  {  
    a += 1;  
  }  
  this.getA = function()  
  {  
    return a;  
  }  
}  

var i = new Constructor();  
var j = new Constructor();  

console.log(i.getA()); // 0  
console.log(j.getA()); // 0  

i.addToA();  

console.log(i.getA()); // 1  
console.log(j.getA()); // 1  

console.log(a); // 1

Since 'a' is now outside of the function, there is only 1 'a', and it is accessible from inside both functions. 'a' is now a 'static variable' - but it's still 'global' - that is, it can be accessed from anywhere. But we can fix that:

var ConstructorClosure = function()  
{  
  var a = 0;  
  var internalConstructor = function()  
  {  

    this.addToA = function()  
    {  
      a += 1;  
    }  
    this.getA = function()  
    {  
      return a;  
    }  
  }  

  return internalConstructor;  
}  

var Constructor = ConstructorClosure();  

var i = new Constructor();  
var j = new Constructor();  

console.log(i.getA()); // 0  
console.log(j.getA()); // 0  

i.addToA();  

console.log(i.getA()); // 1  
console.log(j.getA()); // 1  

console.log(a); // undefined

This works because the ConstructorClosure creates the closure for the variable 'a'. And since it returns a function itself, it's result, which is stored into the variable 'Constructor' (rather than 'Constructor' being directly defined as above), and can be used just like Constructor above, with the added bonus of having access to a private part of memory. A 'protected static' variable. In JavaScript. I know, right?!