Function rewrite and closure scope

The best thing about JavaScript is its implementation of functions.

Functions are first-class objects. As such they can be constructed at run-time, passed as arguments to other functions, returned from a subroutine, or assigned into variables, objects, and arrays.
These features provide the base for functional programming but require a better understanding of dynamic scoping.

JavaScript has function scope, meaning that local variables can be created inside a function. These local variables are accessible only inside that function and are not available any longer when the function returns.
Local variables override global variables.

Let’s see an example

var ImGlobal = true;

(function doSomething () {
   var ImGlobal = false;

   while (true) {
      console.log(ImGlobal); // false

      var ImLocal = true;
      break;
   }

   console.log(ImLocal); // true
})();

console.log(ImGlobal); // true

In languages with block scope, like C, assuming that the same code would run, ImLocal would be “undefined” (null) as it exists only inside the while block.

console.log(ImLocal); // null

* Note that JavaScript 1.7 has block scope with the statement let.

Having first-class functions, in JavaScript a function can be defined within another function. The inner function is created every time the outer function is invoked.
The scope of the outer function becomes part of the internal state of each inner function object, even after the outer function returns. Inner functions are thus able to access any local variable declared in the outer function, with the only exception of this and arguments.
This is called a closure.

Closure is an extremely powerful feature, you can read more about it in many articles, let’s have a look at a simple piece of code

var ImGlobal = true;

function createLogger () {
   var ImGlobal = false;

   return function () {
      console.log(ImGlobal);
   }
};

var logger = createLogger();

console.log(ImGlobal); // true
logger(); // false

What happens in this case?
When you call createLogger a new local scope is created, any new variable declared inside the function lives in that scope.
The function returned by createLogger now has two different scopes, the closure scope is the local scope created when calling createLogger.

Closure scope (and all variables defined in it) survives as long as the functions linked to it are still available.
In the example above, the local variable ImGlobal = false continues to exist even after createLogger returns.

This allow for more advanced design patterns, but it might as well lead to memory leaks.
Let’s imagine a Dom utility with a method to handle browser quirks (not so difficult to imagine)

Dom.preventQuirks = function () {
   if (Browser.isIE) {
      // some code here
   } else {
      // some other code here
   }
};

This code works perfectly fine but it has a performance issue.
Every time you call preventQuirks you’ll have to check whether Browser.isIE is true or not, and there’s no reasonable use case in which that value might change during the lifecycle of your application.

To overcome this you might change the code to look like this

Dom.preventQuirks = Browser.isIE ? function () {
   // some code here
} : function () {
   // some other code here
};

By doing this Browser.isIE is evaluated only once when the function is defined.
The problem with this approach is that Browser must be available when Dom is defined. In frameworks like Aria Templates where a class can specify its own dependencies, it means that Browser should be already loaded before Dom is defined. The Aria Templates dependency management can’t be used in this case.
* Note that improvements are planned in next generation of Aria Templates in order to solve this problem.

Another possible implementation using function rewrite is the following

Dom.preventQuirks = function () {
   if (Browser.isIE) {
      // replace preventQuirks with a different function
      this.preventQuirks = function () {
         // some code here
      };
   } else {
      // replace preventQuirks with a different function
      this.preventQuirks = function () {
         // some other code here
      };
   }

   // and call it
   return this.preventQuirks();
};

In this case, Browser.isIE is still called only once, the first time you call preventQuirks. Subsequent calls will run a different function that replaces the original preventQuirks function.

Doing this, we declare inner functions inside preventQuirks, meaning that a closure scope is created.
We finally get to the point. The newly created function has a closure scope. Let’s consider this case

Dom.handleEvent = function (event) {
   if (Browser.isIE) {
      this.handleEvent = function (event) {
         // some code here
      };
   } else {
      this.handleEvent = function (event) {
         // some other code here
      };
   }

   return this.handleEvent(event);
};

When the new handleEvent gets created, the local scope of the first call survives until Dom is destroyed as handleEvent has been attached to it. And in the closure scope, the initial event is still available.

Such a simple use case does not create a closure nor introduces a memory leak in most browsers.
Any good garbage collector detects that the initial event cannot be accessed in any way and should be collected, however this is not the case in older versions of Internet Explorer.

While tracing a memory leak in Internet Explorer I stumbled across this code

Panel.init = function () {
   // do something here

   this.init = function () {};
   // an empty function, so that in case I call again .init() nothing happens
};

The graph below shows the memory usage of Internet Explorer 8 in a scenario in which the Panel object is repeatedly created, initialized and destroyed.

Take away lesson, among the 3 patterns showed so far, the one I’d suggest is this fourth:

Dom.handleEvent = function (event) {
   if (Browser.isIE) {
      this.handleEvent = this.handleEventOnIE;
   } else {
      this.handleEvent = this.handleEventOnOthers;
   }

   return this.handleEvent(event);
};

Dom.handleEventOnIE = function (event) {
   // some code here
};
Dom.handleEventOnOthers = function (event) {
   // some other code here
};

And regarding the memory leak mentioned before, the solution is very simple

var empty = function () {};

Panel.init = function () {
   // do something here

   this.init = empty;
   // an empty function, so that in case I call again .init() nothing happens
};


When writing code, please think about closures.
Try not to create new functions in the local scope of another function.
If you need to, set to null any variable that is not needed by the inner function.
If available use already defined functions.

Aria Templates provides the following functions for your convenience

// Empty function
Aria.empty = function () {};

// Always return true
Aria.returnTrue = function () {
   return true;
};

// Always return false
Aria.returnFalse = function () {
   return false;
};

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>