JavaScript shortcuts

    If you use JavaScript, but you still haven’t figured out what kind of a wonderful thing this is - closures , and why you need it - this article is for you.

    As you know, in JavaScript the scope of local variables (declared by the word var) is the body of the function within which they are defined.

    If you declare a function inside another function, the former gets access to the variables and arguments of the latter:
    function outerFn (myArg) {
       var myVar;
       function innerFn () {
          // has access to myVar and myArg
       }
    }
    At the same time, such variables continue to exist and remain accessible by the internal function even after the external function in which they are defined has been executed.

    Consider an example - a function that returns the number of its own calls:
    function createCounter () {
       var numberOfCalls = 0;
       return function () {
          return ++ numberOfCalls;
       }
    }
    var fn = createCounter ();
    fn (); // 1
    fn (); // 2
    fn (); // 3
    In this example, the function returned by createCounter uses the variable numberOfCalls, which stores the desired value between its calls (instead of immediately ceasing to exist with the return of createCounter).

    It is for these properties that such "nested" functions in JavaScript are called closures (a term that comes from functional programming languages) - they "lock" the variables and arguments of the function inside which are defined.

    Applying closures


    Let us simplify the example above a bit - remove the need to call the createCounter function separately, making it anonymous and calling immediately after its declaration:
    var fn = (function () {
       var numberOfCalls = 0;
       return function () {
          return ++ numberOfCalls;
       }
    }) ();
    This design allowed us to bind to the function the data stored between its calls - this is one of the applications of closures. In other words, with the help of them we can create functions that have their mutable state.

    Another good use of closures is the creation of functions, which in turn also create functions - something that some would call the technique of so-called. metaprogramming. For instance:
    var createHelloFunction = function (name) {
       return function () {
          alert ('Hello,' + name);
       }
    }
    var sayHelloHabrahabr = createHelloFunction ('Habrahabr');
    sayHelloHabrahabr (); // alerts "Hello, Habrahabr"
    Thanks to the closure, the returned function “remembers” the parameters passed to the creating function , which is what we need for this kind of thing.

    A similar situation occurs when we do not return the internal function, but hang it on some event - since the event arises after the function has been executed, the closure again helps not to lose the data transferred when the handler was created.

    Consider a slightly more complex example - a method that binds a function to a specific context (i.e., an object that the word this will point to in it).
    Function.prototype.bind = function (context) {
       var fn = this;
       return function () {
          return fn.apply (context, arguments);
       };
    }
    var HelloPage = {
       name: 'Habrahabr',
       init: function () {
          alert ('Hello,' + this.name);
       }
    }
    //window.onload = HelloPage.init; // would alert undefined because this would point to window
    window.onload = HelloPage.init.bind (HelloPage); // now everything works
    In this example, using closures, the function returned by bind remembers the initial function and the context assigned to it.

    The next, fundamentally different application of closures is data protection (encapsulation). Consider the following construction:
    (function () {
       ...
    }) ();
    Obviously, inside the closure we have access to all external data, but at the same time it has its own. Thanks to this, we can surround parts of the code with a similar construction in order to close local variables that get inside, from access from the outside. (You can see one example of its use in the source code of the jQuery library, which surrounds all its code with a closure, so as not to push the variables it needs only).

    There is, however, one trap associated with such an application - inside the closure, the meaning of the word this is lost outside. It is solved as follows:
    (function () {
       // parent this will be saved
    }). call (this);

    Consider another trick from the same series. Its developers have widely popularized the Yahoo UI framework, calling it the “Module Pattern” and writing an entire article about it on the official blog .

    Let us have an object ( singleton ) containing any methods and properties:
    var MyModule = {
       name: 'Habrahabr',
       sayPreved: function (name) {
          alert ('PREVED' + name.toUpperCase ())
       },   
       sayPrevedToHabrahabr: function () {
          this.sayPreved (this.name);
       }
    }
    MyModule.sayPrevedToHabrahabr ();
    With the help of closure, we can make methods and properties that are not used outside the object private (that is, accessible only to him):
    var MyModule = (function () {
       var name = 'Habrahabr';
       function sayPreved () {
          alert ('PREVED' + name.toUpperCase ());
       }
       return {
          sayPrevedToHabrahabr: function () {
             sayPreved (name);
          }
       }
    } ) ();
    MyModule.sayPrevedToHabrahabr (); // alerts "PREVED Habrahabr"

    Finally, I want to describe a common mistake that drives many into a stupor in case of ignorance of how the closures work.

    Let us have an array of links, and our task is to make sure that when clicking on each, its serial number is displayed as an alert. The first solution that comes to mind looks like this:
    for (var i = 0; i <links.length; i ++) {
       links [i] .onclick = function () {
          alert (i);
       }
    }
    In fact, it turns out that when you click on any link, the same number is displayed - the value of links.length. Why it happens? Due to the closure, the declared auxiliary variable i continues to exist, and at that moment, when we click on the link. Since by that time the cycle has already passed, i remains equal to the number of links - this is what we see when we click.

    This problem is solved as follows:
    for (var i = 0; i <links.length; i ++) {
       (function (i) {
          links [i] .onclick = function () {
             alert (i);
          }
       }) (i);
    }
    Here, with the help of another closure, we “shadow” the variable i, creating a copy of it in its local scope at each step of the cycle. Thanks to this, everything now works as intended.

    That's all. This article, of course, does not claim to be exhaustive, but I hope someone will still help to figure it out. Thanks for attention!

    Also popular now: