JavaScript & Closures

What is a Closure?

A closure is an inner function that has access to the outer (enclosing) function’s variables—scope chain.

The closure has three scope chains:

  1. It has access to its own scope (variables defined between its curly brackets)
  2. It has access to the outer function‘s variables
  3. and it has access to the global variables

Access to outer variables

What if a variable is accessed, but it isn’t local? Like here:

var bar = 5;

function foo() {
  alert(bars);
}

In this case, the the interpreter finds the variable in the outer lexical scope.

The process consists of two steps:

  1. When a function foo is created, it is not created in an empty space.
    There is a current lexical scope. In the case above, it’s window (bar is undefined at the time of function creation).

    When a function is created, it gets a hidden property, named [[Scope]], which references current lexicl scope.

  2. Later, when the function runs, it creates it’s own lexical scope and links it with [[Scope]].
    So when a variable is not found in the local lexical scope, it is searched outside:

If a variable is read, but can not be found anywhere, the error is generated.

function foo() {
  alert(bar) // reading bar gives error, no bar
}

Certain language constructs block the error, for example typeof bar works if there is no bar (and returns undefined), but that’s an exception.

If a variable is set, but not found anywhere, then it is created in the outmost lexical scope, which is window.

function foo() {
  bar = 5; // writing bar puts it into window
}

Nested functions

Functions can be nested one inside another, forming a chain of lexical scopes which can also be called a scope chain.

var bar = 1;

function foo() {

  function baz() {
    alert(bar);
  }

  return baz;
}

var func = foo()l
func(); // 1

Lexical scope form a chain (from inside out):

// LexicalScope = window = {bar:1, foo: function}
var bar = 1;

function foo() {
  // LexicalScope = {baz:function}

  function baz() {
    // LexicalScope = {}
    alert(bar);
  }

  return baz;
}

So, function baz has access to baz, foo, and bar.

Nested functions may continue to live after the outer function has finished:

function User(name) {
  this.say = function(phrase) {
    alert(name + ' says: ' + phrase);
  }
}

var user = new User('John');

Note, the this context is not related to scopes and variables. It does not participate here.As we see, this.say is a property in the user object, so it continues to live after User completed.

And if you remember, when this.say is created, it (as every function) gets an internal reference to this.say.[[Scope]] to current lexical scope. So, the lexical scope of the current User execution stays in memory. All variables of User also are it’s properties, so they are also carefully kept, not junked as usually.

The whole point is to ensure that if the inner function wants to access an outer variable in the future, it is able to do so.

Mutability of Lexical Scope

Several function may share same outer LexicalEnvironment. In this case they can modify it’s properties.

In the example below, this.fixName changes name, which is used by this.say:

function User(name) {

  this.fixName = function() {
    name = 'Mr.' + name.toUpperCase();
  }

  this.say = function(phrase) {
    alert(name + ' says: ' + phrase);
  }

}

var user = new User('John'); // (1)
user.fixName(); // (2)
user.say("I'm alive!"); // Mr.JOHN says: I'm alive!

Here user.fixName.[[Scope]] and user.say.[[Scope]] reference same lexical scope, which corresponds to new User run.

From (1) to (2), the LexicalScope.name is updated, so both functions see the variable change.

[[Scope]] for new Function

There is an exception to general scope binding rule. When you create a function using new Function, it’s [[Scope]] points to window, not to current lexical scope.

The following example demonstrates how a function, created with new Function ignores local variable a and outputs the global variable.

The regular behavior:

window.a = 1;
function getFunc() {
  var a = 2;

  var func = function() { alert(a) }

  return func;
}

getFunc()() // 2, from LexicalEnvironemnt of getFunc
And now the function, created by :

window.a = 1
function getFunc() {
  var a = 2

  var func = new Function('', 'alert(a)')
  return func
}

getFunc()() // 1, from window

Be first to comment

Leave a Reply