SlideShare uma empresa Scribd logo
1 de 61
Scope, Garbage Collection & Closures
                                              In Javascript




                                                                                     David Semeria
                                                                                     lmframework.com

This work is licensed under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License
© 2010 David Semeria                                                                                          Page 1
Core Concepts

               Although Javscript has block syntax { }, only
               functions can create a new scope within a global
               scope.




© 2010 David Semeria                                              Page 2
Core Concepts

               Although Javscript has block syntax { }, only
               functions can create a new scope within a global
               scope.


               After a function exits, if none of the contents of its
               scope are referenced by other (active) scopes
               then the function's scope is destroyed.




© 2010 David Semeria                                                    Page 3
Core Concepts

               Although Javscript has block syntax { }, only
               functions can create a new scope within a global
               scope.


               After a function exits, if none of the contents of its
               scope are referenced by other (active) scopes
               then the function's scope is destroyed.


               Alternatively, if any of the contents of an exited
               function's scope are referenced in other scopes,
               the function's scope will continue to live on until no
               such references exist.

© 2010 David Semeria                                                    Page 4
In Pictures..
                                                                            Global Scope
     var f = function() {
       ...                                                           Scope for f
       var g = function() {
         ...
                                                              Scope for g
         var h = function() {
           ....                                       Scope for h
           ....
         }
       }
     }



 The post-execution lifetimes of inner scopes depend solely on the
 existence of active external references to their contents.



© 2010 David Semeria                                                                       Page 5
Scope Persistence
                                                                          Global Scope
   var f = function() {
     var obj = { a: “I live in f's scope”,                           Scope for f

                 b: “So do I”
               };
     return obj;
   }
                       External reference to obj from global scope

   var instance = f();



  A scope persists when any of its contents persist.
  Above, f()'s scope persists given the external reference to obj.
  A closure is a special case of scope persistence, and is formed when an
  external reference to a function exists, and that function accesses variables
  from an outer function's scope.

© 2010 David Semeria                                                                     Page 6
Closures
                                                                                     Global Scope
     var f = function() {
       var x = ... ;                                                            Scope for f
       var g = function() {
         var y = ... ;                                                   Scope for g
         var h = function() {
           var z = x + y;                                         Scope for h

           ....                         x and y belong to outer scopes

         }
       }
     }        External reference to h() from global scope


 A closure occurs when a function which was not defined in the global scope
 continues to be referenced (either directly or indirectly) from an object in an
 active scope after the function that created it has finished execution.
 In this case, the referenced function's scope, and those surrounding it,
 persist for as long as any external reference to it exists.
© 2010 David Semeria                                                                                Page 7
Closure ?
                                             Global Scope
   var f = function() {
     alert(“in f”);
     var g = function() {
       alert(“in g”);
     }
     g();
   }


   f();           //two alerts


 Is this a closure?




© 2010 David Semeria                                        Page 8
Closure ?
                                                                 Global Scope
   var f = function() {
     alert(“in f”);
     var g = function() {
       alert(“in g”);
     }
     g();
   }


   f();            //two alerts


  No; f() runs, it calls g(), then both functions exit.
  There are no active references from the global scope to g().




© 2010 David Semeria                                                            Page 9
A Simple Closure
                                                                      Global Scope
    var f = function(x) {
      var m = function(y) {
        return x * y;
      }
      return m;
    }


    var instance = f(z);



We'll see examples of the function working in a moment.
First let's look at the references which exist after f() has terminated....




© 2010 David Semeria                                                                 Page 10
A Simple Closure
                                                                Global Scope
    var f = function(x) {
      var m = function(y) {
        return x * y;
      }
      return m;
    }


    var instance = f(z);




The only external reference we have is instance which points to m().




© 2010 David Semeria                                                           Page 11
A Simple Closure
                                                                     Global Scope
    var f = function(x) {
      var m = function(y) {
        return x * y;
      }
      return m;
    }


    var instance = f(z);



  Within the scope of m() there is a reference to x


  Given that x is the variable we want to “remember”, we can see that it
  continues to live because it is indirectly referenced from the global scope.


  Also, note that x is defined in m()'s parent scope, not m()'s

© 2010 David Semeria                                                                Page 12
A Simple Closure
                                                                     Global Scope
    var f = function(x) {
      var m = function(y) {
        return x * y;
      }
      return m;
    }
    var myDouble = f(2);
    var myTreble = f(3);

    alert (myDouble(10))      // 20
    alert (myTreble(10))      // 30


Each time f() runs, a new scope is created in which a new m() is also
created. As long as a reference to a given m() exists in the global scope,
then m()'s scope, and that of its parent, continue to exist. In this way, each
m() has access to the correct value of x .


© 2010 David Semeria                                                                Page 13
A Simple Closure
                                      (digging deeper)

    var f = function(x) {                                       Global Scope

      var u;
      var m = function(y) {
        return x * y;
      }
      return m;
    }


   What about the variable u ?
   Does it still exist within f() 's scope, after f() exits ?




© 2010 David Semeria                                                           Page 14
A Simple Closure
                                    (digging deeper)

    var f = function(x) {                                              Global Scope

      var u;
      var m = function(y) {
        return x * y;
      }
      return m;
    }


  Probably not. But then again, who cares?
  The garbage collector will see there are no active references to u and will
  therefore destroy it. When we say that a scope persists we don't imply
  that all its contents also do. This is the whole point of garbage collection -
  it destroys only what we have no way of accessing.




© 2010 David Semeria                                                                  Page 15
Using Closures

                       ●   Information hiding (encapsulation)

                       ●   Queued functions (timers)

                       ●   Event Handlers

                       ●   Callbacks


© 2010 David Semeria                                            Page 16
Information Hiding
                                                                    Global Scope
   var f = function(priv,pub) {
     var a = function() { alert(pub) } //         public function
     var b = function() { c() }        //         privileged function
     var c = function() { alert(priv)} //         private function
     return { showPublic : a,
              showPrivate: b }
   }
   var obj = f('PUBLIC','PRIVATE');
   obj.showPublic();            // alert:         PUBLIC
   obj.showPrivate();           // alert:         PRIVATE


  The parameter containing the private information priv is only accessible
  from the outside via the showPrivate() method. Similarly, the private
  function c() is only accessible via the privileged method b() - which is
  exposed as showPrivate().

© 2010 David Semeria                                                               Page 17
Information Hiding
                                      (back-tracing)
                                                                       Global Scope
   var f = function(priv,pub) {
     var a = function() { alert(pub) } //            public function
     var b = function() { c() }        //            privileged function
     var c = function() { alert(priv)} //            private function
     return { showPublic : a,
              showPrivate: b }
   }
   var obj = f('PUBLIC','PRIVATE');
   obj.showPublic();            // alert:            PUBLIC
   obj.showPrivate();           // alert:            PRIVATE



  A useful exercise is to work backwards from the external references and
  check that all the contents we want to persist are either directly or indirectly
  referenced from an active scope.


© 2010 David Semeria                                                                  Page 18
Information Hiding
                                (back-tracing)
                                                          Global Scope
   var f = function(priv,pub) {
     var a = function() { alert(pub) } //    public function
     var b = function() { c() }        //    privileged function
     var c = function() { alert(priv)} //    private function
     return { showPublic : a,
              showPrivate: b }
   }
   var obj = f('PUBLIC','PRIVATE');
   obj.showPublic();            // alert:    PUBLIC
   obj.showPrivate();           // alert:    PRIVATE



Let's start with showPublic()




© 2010 David Semeria                                                     Page 19
Information Hiding
                               (back-tracing)
                                                           Global Scope
   var f = function(priv,pub) {
     var a = function() { alert(pub) } //     public function
     var b = function() { c() }        //     privileged function
     var c = function() { alert(priv)} //     private function
     return { showPublic : a,
              showPrivate: b }
   }
   var obj = f('PUBLIC','PRIVATE');
   obj.showPublic();            // alert:     PUBLIC
   obj.showPrivate();           // alert:     PRIVATE



We can see that showPublic() references a()




© 2010 David Semeria                                                      Page 20
Information Hiding
                              (back-tracing)
                                                         Global Scope
   var f = function(priv,pub) {
     var a = function() { alert(pub) } //   public function
     var b = function() { c() }        //   privileged function
     var c = function() { alert(priv)} //   private function
     return { showPublic : a,
              showPrivate: b }
   }
   var obj = f('PUBLIC','PRIVATE');
   obj.showPublic();            // alert:   PUBLIC
   obj.showPrivate();           // alert:   PRIVATE



And a() references pub




© 2010 David Semeria                                                    Page 21
Information Hiding
                                (back-tracing)
                                                          Global Scope
   var f = function(priv,pub) {
     var a = function() { alert(pub) } //    public function
     var b = function() { c() }        //    privileged function
     var c = function() { alert(priv)} //    private function
     return { showPublic : a,
              showPrivate: b }
   }
   var obj = f('PUBLIC','PRIVATE');
   obj.showPublic();            // alert:    PUBLIC
   obj.showPrivate();           // alert:    PRIVATE



And now showPrivate(), which directly references b().




© 2010 David Semeria                                                     Page 22
Information Hiding
                              (back-tracing)
                                                         Global Scope
   var f = function(priv,pub) {
     var a = function() { alert(pub) } //   public function
     var b = function() { c() }        //   privileged function
     var c = function() { alert(priv)} //   private function
     return { showPublic : a,
              showPrivate: b }
   }
   var obj = f('PUBLIC','PRIVATE');
   obj.showPublic();            // alert:   PUBLIC
   obj.showPrivate();           // alert:   PRIVATE



And b() references c()




© 2010 David Semeria                                                    Page 23
Information Hiding
                               (back-tracing)
                                                         Global Scope
   var f = function(priv,pub) {
     var a = function() { alert(pub) } //   public function
     var b = function() { c() }        //   privileged function
     var c = function() { alert(priv)} //   private function
     return { showPublic : a,
              showPrivate: b }
   }
   var obj = f('PUBLIC','PRIVATE');
   obj.showPublic();            // alert:   PUBLIC
   obj.showPrivate();           // alert:   PRIVATE



And c() references priv




© 2010 David Semeria                                                    Page 24
Information Hiding
                                     (back-tracing)
                                                                     Global Scope
   var f = function(priv,pub) {
     var a = function() { alert(pub) } //           public function
     var b = function() { c() }        //           privileged function
     var c = function() { alert(priv)} //           private function
     return { showPublic : a,
              showPrivate: b }
   }
   var obj = f('PUBLIC','PRIVATE');
   obj.showPublic();            // alert:           PUBLIC
   obj.showPrivate();           // alert:           PRIVATE


  We can see that all the information within the scope of f() (including its
  parameters) are referenced either directly or indirectly from the global
  scope. This is actually no surprise, because if a variable was not accessible
  from the outside, we wouldn't care whether it existed or not.

© 2010 David Semeria                                                                Page 25
Information Hiding
                                        (modify once)
   var f = function(state) {                            Global Scope
     var mod = function(newState){
                 state = newState;
                 mod   = function(){}      // redefine mod
                }
     var get = function(){ return state }
     return { modify:   function(s){mod(s)},
              getState: get }
   }



 This function uses a closure to store a state variable which can be
 modified only once. After the first time state is modified, the function which
 permits its modification (mod) redefines itself (perhaps to return an error).
 Further details in the next slide...



© 2010 David Semeria                                                              Page 26
Information Hiding
                                (modify once)
   var f = function(state) {                            Global Scope
     var mod = function(newState){
                 state = newState;
                 mod   = function(){}      // redefine mod
                }
     var get = function(){ return state }
     return { modify:   function(s){mod(s)},
              getState: get }
   }
   var obj = f('state_1');

   alert(obj.getState())     // state_1

   obj.modify('state_2');

   alert(obj.getState())     // state_2

   obj.modify('state_3');

   alert(obj.getState())     // state_2

© 2010 David Semeria                                                   Page 27
Information Hiding
                                    (modify once)
   var f = function(state) {                            Global Scope
     var mod = function(newState){
                 state = newState;
                 mod   = function(){}      // redefine mod
                }
     var get = function(){ return state }
     return { modify:   function(s){mod(s)},
              getState: get }
   }


    Care must be taken not to leave dangling references to objects we wish
    to remove / redefine.


    This is the reason why mod is exposed as function(s){mod(s)} and
    not as a simple reference to mod.


© 2010 David Semeria                                                         Page 28
Information Hiding
                                     (modify once)
   var f = function(state) {                            Global Scope
     var mod = function(newState){
                 state = newState;
                 mod   = function(){}      // redefine mod
                }
     var get = function(){ return state }
     return { modify:   function(s){mod(s)},
              getState: get }
   }



  After mod is redefined, there are no longer any references to the original
  function, and so its is destroyed by the garbage collector.
  Hence, this pattern is also useful for removing module initialization code
  once it has run.



© 2010 David Semeria                                                           Page 29
Using Closures

                       ●   Information hiding (encapsulation)

                       ●   Queued functions (timers)

                       ●   Event Handlers

                       ●   Callbacks


© 2010 David Semeria                                            Page 30
Queued Functions
   var fade = function(e,delay) {                                  Global Scope

     e.style.opacity = 1;
     var proc = function() {
       if (e.style.opacity >= 0.1) {
         e.style.opacity -= 0.1;
         setTimeout(proc,delay);
       }
       else {
         e.style.opacity = 0;
         }
     }
     proc();
   }

  The key point here is that the garbage collector (for very good reasons)
  considers queued functions (inserted via setTimeout and setInterval) to
  be part of the global scope.
© 2010 David Semeria                                                              Page 31
Queued Functions
   var fade = function(e,delay) {                               Global Scope

     e.style.opacity = 1;
     var proc = function() {
       if (e.style.opacity >= 0.1) {
         e.style.opacity -= 0.1;
         setTimeout(proc,delay);
       }
       else {
         e.style.opacity = 0;
         }
     }
     proc();
   }

  This means that the variables we need to remember (e and delay)
  continue to exist within fade() and proc()'s scopes given the queued
  reference to proc().
© 2010 David Semeria                                                           Page 32
Using Closures

                       ●   Information hiding (encapsulation)

                       ●   Queued functions (timers)

                       ●   Event Handlers

                       ●   Callbacks


© 2010 David Semeria                                            Page 33
Event Handlers
                                    ( good example )
   var attach = function(e) {                                               Global Scope

     for (var i=0; i < e.ChildNodes.length; i++) {
       e.ChildNodes.length[i].onclick = function(x){
           return function(evt){
             alert(x+1)
           }
        }(i);
     }
   }

   [1] [2] [3]


This function takes an element and attaches to each of its child nodes an
onclick function which alerts its position in the list (starting from 1).
To demonstrate the function working, we create three spans ([1],[2],[3])
and pass their parent node to attach(), as shown next..

© 2010 David Semeria                                                                       Page 34
Event Handlers
                                       ( good example )
   var attach = function(e) {                             Global Scope

     for (var i=0; i < e.ChildNodes.length; i++) {
       e.ChildNodes.length[i].onclick = function(x){
           return function(evt){
             alert(x+1)
           }
        }(i);
     }
   }

   [1] [2] [3]



                       Onclick alert

                       1




© 2010 David Semeria                                                     Page 35
Event Handlers
                              ( good example )
   var attach = function(e) {                          Global Scope

     for (var i=0; i < e.ChildNodes.length; i++) {
       e.ChildNodes.length[i].onclick = function(x){
           return function(evt){
             alert(x+1)
           }
        }(i);
     }
   }

   [1] [2] [3]



                       Onclick alert

                       3




© 2010 David Semeria                                                  Page 36
Event Handlers
                                   ( good example )
   var attach = function(e) {                                         Global Scope

     for (var i=0; i < e.ChildNodes.length; i++) {
       e.ChildNodes.length[i].onclick = function(x){
           return function(evt){
             alert(x+1)
           }
        }(i);
     }
   }

   [1] [2] [3]

 This example is often used to show applications of closures in event
 handlers (we'll see how it works in a moment). In reality, it's not a closure -
 because the inner function contains no references to the scope of the outer
 one. The above function actually provides a workaround for an unwanted
 closure created by an incorrect implementation, which is shown next.
© 2010 David Semeria                                                                 Page 37
Event Handlers
                             ( bad example )
   var attach = function(e) {                         Global Scope

     for (var i=0; i < e.ChildNodes.length; i++) {
       e.ChildNodes.length[i].onclick = function(){
         alert(i+1)
       }
     }
   }


   [1] [2] [3]




        Onclick alert

      4




© 2010 David Semeria                                                 Page 38
Event Handlers
                                     ( bad example )
   var attach = function(e) {                          Global Scope

     for (var i=0; i < e.ChildNodes.length; i++) {
       e.ChildNodes.length[i].onclick = function(){
         alert(i+1)
       }
     }
   }


   [1] [2] [3]




                   Onclick alert

                 4




© 2010 David Semeria                                                  Page 39
Event Handlers
                                       ( bad example )
   var attach = function(e) {                            Global Scope

     for (var i=0; i < e.ChildNodes.length; i++) {
       e.ChildNodes.length[i].onclick = function(){
         alert(i+1)
       }
     }
   }


   [1] [2] [3]




                       Onclick alert

                       4




© 2010 David Semeria                                                    Page 40
Event Handlers
                                   ( bad example )
   var attach = function(e) {                                        Global Scope

     for (var i=0; i < e.ChildNodes.length; i++) {
       e.ChildNodes.length[i].onclick = function(){
         alert(i+1)
       }
     }
   }


   [1] [2] [3]


 This incorrect behaviour stems from the accidental creation of a closure.
 The i used in each onclick function is a reference to the same i used in the
 for loop. This reference creates the unwanted closure, meaning the for
 loop's i continues to live on (with the value which caused the loop to
 terminate) in each of the created onclick functions.


© 2010 David Semeria                                                                Page 41
Event Handlers
                                  ( good example )
   var attach = function(e) {                                        Global Scope

     for (var i=0; i < e.ChildNodes.length; i++) {
       e.ChildNodes.length[i].onclick = function(x){
           return function(evt){
             alert(x+1)
           }
        }(i);
     }
   }

   [1] [2] [3]

 Back to the correct version. The unwanted effects of the closure are
 avoided by passing i to a function which is invoked immediately (note the
 (i) at the end). This means that the x used in the new onclick's function is
 bound to the correct value of i each time. And if we really want to use a
 closure, we can use the example which follows.
© 2010 David Semeria                                                                Page 42
Event Handlers
                            ( using a closure )
  var attach = function(e) {                          Global Scope

    for (var i=0; i < e.ChildNodes.length; i++) {
      var msg = 'hello from ';
      e.ChildNodes.length[i].onclick = function(x){
          return function(evt){
            alert(msg+(x+1))
          }
       }(i);
    }
    return function (m) {msg = m};
  }
  var mod = attach(elem);
  [1] [2] [3]
                       Onclick alert

                       hello from 3




© 2010 David Semeria                                                 Page 43
Event Handlers
                                   ( using a closure )
  var attach = function(e) {                                           Global Scope

    for (var i=0; i < e.ChildNodes.length; i++) {
      var msg = 'hello from ';
      e.ChildNodes.length[i].onclick = function(x){
          return function(evt){
            alert(msg+(x+1))
          }
       }(i);
    }
    return function (m) {msg = m};
  }
  var mod = attach(elem);
  [1] [2] [3]


    Here, we create a variable - msg - within the scope of attach() . We then
    create a closure by referencing msg directly in the onclick function.


© 2010 David Semeria                                                                  Page 44
Event Handlers
                                ( using a closure )
  var attach = function(e) {                                       Global Scope

    for (var i=0; i < e.ChildNodes.length; i++) {
      var msg = 'hello from ';
      e.ChildNodes.length[i].onclick = function(x){
          return function(evt){
            alert(msg+(x+1))
          }
       }(i);
    }
    return function (m) {msg = m};
  }
  var mod = attach(elem);
  [1] [2] [3]


 By returning an access function for msg, we can change it's value from the
 outside...


© 2010 David Semeria                                                              Page 45
Event Handlers
                           ( using a closure )
  var attach = function(e) {                          Global Scope

    for (var i=0; i < e.ChildNodes.length; i++) {
      var msg = 'hello from ';
      e.ChildNodes.length[i].onclick = function(x){
          return function(evt){
            alert(msg+(x+1))
          }
       }(i);
    }
    return function (m) {msg = m};
  }
  var mod = attach(elem);
  mod('goodbye from ');
  [1] [2] [3]             Onclick alert

                        goodbye from 3




© 2010 David Semeria                                                 Page 46
Using Closures

                       ●   Information hiding (encapsulation)

                       ●   Queued functions (timers)

                       ●   Event Handlers

                       ●   Callbacks


© 2010 David Semeria                                            Page 47
Callbacks
                                      Global Scope
  var replace = function(e,ref) {
    var callback = function(t) {
      e.innerHTML = t;
    }
    makeXHR(callback,ref); // async
  }
  replace(elem,ref);




© 2010 David Semeria                                 Page 48
Callbacks
                                                                   Global Scope
  var replace = function(e,ref) {
    var callback = function(t) {
      e.innerHTML = t;
    }
    makeXHR(callback,ref); // async
  }                       External reference     to callback from global scope
  replace(elem,ref);



By now it should be clear what is happening. The makeXHR() call returns
immediately and maintains a reference to callback(), which in turn retains
a reference to e. Even though replace() exits immediately, the contents of
the closure will persist until they are needed by the callback() function.




© 2010 David Semeria                                                              Page 49
Callbacks
                                                                     Global Scope
  var replace = function(e,ref) {
    var callback = function(t) {
      e.innerHTML = t;
    }
    makeXHR(callback,ref); // async
  }
  replace(elem,ref);


 This simple example demonstrates the power of closures.


 Even though we pass one simple reference to makeXHR(), we can
 elegantly store as much context-specific information as we like in the
 closure, safe in the knowledge that it will available when we need it.




© 2010 David Semeria                                                                Page 50
This & That
                                     ( overview )

         ●    Many discussions of closures frequently get bogged down
              with issues regarding the self-referential object pointer
              this. Consequently, we have avoided it so far.


         ●    Under certain circumstances, the use of this can lead to
              unintended behaviour - examples of which are provided in
              the following sides.


         ●    That said, sometimes this can be very useful (if not
              essential), and so we'll also present a few examples of how
              this can usefully be employed in closures.




© 2010 David Semeria                                                        Page 51
This & That
                                   ( problems with 'this' )
                                                                          Global Scope
  var F = function() {
    this.x = 1;
    if (this === window) alert(“'this' is bound to window”);
    if (this.x) alert(“'this' is bound to new object”);
  }


  var obj = new F(); // alert: 'this' is bound to new object
  var obj = F();     // alert: 'this' is bound to window


     ●    It is important to bear in mind that this is only bound to the object
          being created by a constructor function when new is used.




© 2010 David Semeria                                                                     Page 52
This & That
                                  ( object construction )
                                                                       Global Scope
  var f = function(a,b,c) {
    var obj = new function(){
      this.a = a;
      this.b = b;
    }
    return { d: obj,
             c: c }
  }
  var newObj = f('a','b','c');


    ●    For this reason it is often better to enclose new within a general
         function, which no longer needs to be called using new. Also, objects
         can easily be created without using new, for example by using object
         literals {}
    ●    The contrived example above employs both these techniques



© 2010 David Semeria                                                                  Page 53
This & That
                             ( when 'this' is useful )
                                                                   Global Scope


  var myOnclick = function(evt) {
    alert(“my id is “ + this.id);
  }


  elem.onclick = myOnclick;




Sometimes, however, the use of this is either unavoidable or useful (or
both). Examples are situations when functions are attached to objects,
such as event handlers.


But it is important to remember that each function may access a different
this – a situation that can lead to unexpected behaviour, as shown next.


© 2010 David Semeria                                                              Page 54
This & That
                                   ( bad example )

 var multiply = function(list){                           Global Scope

   for (var i=0; i<list.length; i++) {
     list[i].onclick = function(e){
       var newE = document.createElement('div');
       this.appendChild(newE);
       newE.onclick = function(e){
         alert('created by '+this.id); //this is wrong
     }
   }
 }
 elem.onclick = multiply(lst); // lst references 3 blue divs


The purpose of this function is to take an array of elements (list) and
assign a new onlclick function to each. The onclick function will create a
new div within the clicked element and assign to the new div an onclick
function which will alert the id of the element which created it.

© 2010 David Semeria                                                         Page 55
This & That
                                 ( bad example )

 var multiply = function(list){                           Global Scope

   for (var i=0; i<list.length; i++) {
     list[i].onclick = function(e){
       var newE = document.createElement('div');
       this.appendChild(newE);
       newE.onclick = function(e){
         alert('created by '+this.id); //this is wrong
     }
   }
 }
 elem.onclick = multiply(lst); // lst references 3 blue divs

Unfortunately, the function does not work as hoped.
In the following slide, we create three blue divs and pass them (as the
members of list) to the above function. We assume the rightmost blue div
has already been clicked, thus creating a new (red) div - which does not
report its creator correctly.
© 2010 David Semeria                                                       Page 56
This & That
                                        ( bad example )

 var multiply = function(list){                           Global Scope

   for (var i=0; i<list.length; i++) {
     list[i].onclick = function(e){
       var newE = document.createElement('div');
       this.appendChild(newE);
       newE.onclick = function(e){
         alert('created by '+this.id); //this is wrong
     }
   }                                           Onclick alert
 }
                                              Created by undefined
 elem.onclick = multiply(lst); // lst references 3 blue divs

     id: blue_div_1    id: blue_div_2    id: blue_div_3




© 2010 David Semeria                                                     Page 57
This & That
                                  ( bad example )

 var multiply = function(list){                           Global Scope

   for (var i=0; i<list.length; i++) {
     list[i].onclick = function(e){
       var newE = document.createElement('div');
       this.appendChild(newE);
       newE.onclick = function(e){
         alert('created by '+this.id); //this is wrong
     }
   }
 }
 elem.onclick = multiply(lst); // lst references 3 blue divs

The reason for the incorrect behaviour stems from the fact that the
innermost onclick function also creates its own this – thereby obscuring
the this we were hoping to use from the outer scope.



© 2010 David Semeria                                                       Page 58
This & That
                                 ( good example )

 var multiply = function(list){                           Global Scope

   for (var i=0; i<list.length; i++) {
     list[i].onclick = function(e){
       var newE = document.createElement('div');
       this.appendChild(newE);
       var that = this;
       newE.onclick = function(e){
         alert('created by '+that.id); //this is right
     }
   }
 }
 elem.onclick = multiply(lst); // lst references 3 blue divs


Fortunately, the solution is straightforward. We merely create a reference to
the outer this – by convention named that – which will be available to the
inner scope.


© 2010 David Semeria                                                            Page 59
This & That
                                        ( good example )

 var multiply = function(list){                           Global Scope

   for (var i=0; i<list.length; i++) {
     list[i].onclick = function(e){
       var newE = document.createElement('div');
       this.appendChild(newE);
       var that = this;
       newE.onclick = function(e){
         alert('created by '+that.id); //this is right
     }
   }                                          Onclick alert
 }
                                             Created by blue_div_3
 elem.onclick = multiply(lst); // lst references 3 blue divs
  id: blue_div_1       id: blue_div_2   id: blue_div_3




© 2010 David Semeria                                                     Page 60
Comments are welcome, please leave them here




      David Semeria                                      http://lmframework.com
      February 2010                                      http://twitter.com/hymanroth



© 2010 David Semeria                                                              Page 61

Mais conteúdo relacionado

Mais procurados

Customizing nopCommerce with Plugins and Themes
Customizing nopCommerce with Plugins and ThemesCustomizing nopCommerce with Plugins and Themes
Customizing nopCommerce with Plugins and Themes
Gaines Kergosien
 
Introduction to Java Programming Language
Introduction to Java Programming LanguageIntroduction to Java Programming Language
Introduction to Java Programming Language
jaimefrozr
 

Mais procurados (20)

3. Java Script
3. Java Script3. Java Script
3. Java Script
 
JavaScript Event Loop
JavaScript Event LoopJavaScript Event Loop
JavaScript Event Loop
 
Customizing nopCommerce with Plugins and Themes
Customizing nopCommerce with Plugins and ThemesCustomizing nopCommerce with Plugins and Themes
Customizing nopCommerce with Plugins and Themes
 
JavaScript Inheritance
JavaScript InheritanceJavaScript Inheritance
JavaScript Inheritance
 
JavaScript for ABAP Programmers - 7/7 Functional Programming
JavaScript for ABAP Programmers - 7/7 Functional ProgrammingJavaScript for ABAP Programmers - 7/7 Functional Programming
JavaScript for ABAP Programmers - 7/7 Functional Programming
 
Fetch API Talk
Fetch API TalkFetch API Talk
Fetch API Talk
 
An Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersAn Introduction to Gradle for Java Developers
An Introduction to Gradle for Java Developers
 
This keyword and final keyword
This keyword and final  keywordThis keyword and final  keyword
This keyword and final keyword
 
Introduction to Java Programming Language
Introduction to Java Programming LanguageIntroduction to Java Programming Language
Introduction to Java Programming Language
 
React workshop presentation
React workshop presentationReact workshop presentation
React workshop presentation
 
Node.js
Node.jsNode.js
Node.js
 
JQuery UI
JQuery UIJQuery UI
JQuery UI
 
java 8 new features
java 8 new features java 8 new features
java 8 new features
 
L'API Collector dans tous ses états
L'API Collector dans tous ses étatsL'API Collector dans tous ses états
L'API Collector dans tous ses états
 
JavaScript Programming
JavaScript ProgrammingJavaScript Programming
JavaScript Programming
 
Java Script ppt
Java Script pptJava Script ppt
Java Script ppt
 
Elements of Java Language
Elements of Java Language Elements of Java Language
Elements of Java Language
 
JavaScript for ABAP Programmers - 3/7 Syntax
JavaScript for ABAP Programmers - 3/7 SyntaxJavaScript for ABAP Programmers - 3/7 Syntax
JavaScript for ABAP Programmers - 3/7 Syntax
 
为啥别读HotSpot VM的源码(2012-03-03)
为啥别读HotSpot VM的源码(2012-03-03)为啥别读HotSpot VM的源码(2012-03-03)
为啥别读HotSpot VM的源码(2012-03-03)
 
Spring Framework - Data Access
Spring Framework - Data AccessSpring Framework - Data Access
Spring Framework - Data Access
 

Semelhante a Closures in Javascript

CodeFest 2014. Axel Rauschmayer — JavaScript’s variables: scopes, environment...
CodeFest 2014. Axel Rauschmayer — JavaScript’s variables: scopes, environment...CodeFest 2014. Axel Rauschmayer — JavaScript’s variables: scopes, environment...
CodeFest 2014. Axel Rauschmayer — JavaScript’s variables: scopes, environment...
CodeFest
 
Javascript basic course
Javascript basic courseJavascript basic course
Javascript basic course
Tran Khoa
 
Implementation of interface9 cm604.30
Implementation of interface9 cm604.30Implementation of interface9 cm604.30
Implementation of interface9 cm604.30
myrajendra
 
Scripting
ScriptingScripting
Scripting
aztack
 
java-06inheritance
java-06inheritancejava-06inheritance
java-06inheritance
Arjun Shanka
 

Semelhante a Closures in Javascript (20)

CodeFest 2014. Axel Rauschmayer — JavaScript’s variables: scopes, environment...
CodeFest 2014. Axel Rauschmayer — JavaScript’s variables: scopes, environment...CodeFest 2014. Axel Rauschmayer — JavaScript’s variables: scopes, environment...
CodeFest 2014. Axel Rauschmayer — JavaScript’s variables: scopes, environment...
 
The JavaScript Programming Primer
The JavaScript  Programming PrimerThe JavaScript  Programming Primer
The JavaScript Programming Primer
 
Javascript basic course
Javascript basic courseJavascript basic course
Javascript basic course
 
[2015/2016] JavaScript
[2015/2016] JavaScript[2015/2016] JavaScript
[2015/2016] JavaScript
 
Javascript internals
Javascript internalsJavascript internals
Javascript internals
 
Java script Techniques Part I
Java script Techniques Part IJava script Techniques Part I
Java script Techniques Part I
 
Constructor and destructor
Constructor and destructorConstructor and destructor
Constructor and destructor
 
Java scriptconfusingbits
Java scriptconfusingbitsJava scriptconfusingbits
Java scriptconfusingbits
 
Java scriptconfusingbits
Java scriptconfusingbitsJava scriptconfusingbits
Java scriptconfusingbits
 
JavaScript Closures for Dummies & JavaScript prototype, closures and OOP.
JavaScript Closures for Dummies & JavaScript prototype, closures and OOP.JavaScript Closures for Dummies & JavaScript prototype, closures and OOP.
JavaScript Closures for Dummies & JavaScript prototype, closures and OOP.
 
Gradle in a Polyglot World
Gradle in a Polyglot WorldGradle in a Polyglot World
Gradle in a Polyglot World
 
Clojure: Functional Concurrency for the JVM (presented at OSCON)
Clojure: Functional Concurrency for the JVM (presented at OSCON)Clojure: Functional Concurrency for the JVM (presented at OSCON)
Clojure: Functional Concurrency for the JVM (presented at OSCON)
 
Groovy: to Infinity and Beyond -- JavaOne 2010 -- Guillaume Laforge
Groovy: to Infinity and Beyond -- JavaOne 2010 -- Guillaume LaforgeGroovy: to Infinity and Beyond -- JavaOne 2010 -- Guillaume Laforge
Groovy: to Infinity and Beyond -- JavaOne 2010 -- Guillaume Laforge
 
Implementation of interface9 cm604.30
Implementation of interface9 cm604.30Implementation of interface9 cm604.30
Implementation of interface9 cm604.30
 
Scripting
ScriptingScripting
Scripting
 
JavaScript global object, execution contexts & closures
JavaScript global object, execution contexts & closuresJavaScript global object, execution contexts & closures
JavaScript global object, execution contexts & closures
 
ClojureScript - A functional Lisp for the browser
ClojureScript - A functional Lisp for the browserClojureScript - A functional Lisp for the browser
ClojureScript - A functional Lisp for the browser
 
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)
 
java-06inheritance
java-06inheritancejava-06inheritance
java-06inheritance
 
subash.ppt.pptx
subash.ppt.pptxsubash.ppt.pptx
subash.ppt.pptx
 

Último

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Último (20)

ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 

Closures in Javascript

  • 1. Scope, Garbage Collection & Closures In Javascript David Semeria lmframework.com This work is licensed under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License © 2010 David Semeria Page 1
  • 2. Core Concepts Although Javscript has block syntax { }, only functions can create a new scope within a global scope. © 2010 David Semeria Page 2
  • 3. Core Concepts Although Javscript has block syntax { }, only functions can create a new scope within a global scope. After a function exits, if none of the contents of its scope are referenced by other (active) scopes then the function's scope is destroyed. © 2010 David Semeria Page 3
  • 4. Core Concepts Although Javscript has block syntax { }, only functions can create a new scope within a global scope. After a function exits, if none of the contents of its scope are referenced by other (active) scopes then the function's scope is destroyed. Alternatively, if any of the contents of an exited function's scope are referenced in other scopes, the function's scope will continue to live on until no such references exist. © 2010 David Semeria Page 4
  • 5. In Pictures.. Global Scope var f = function() { ... Scope for f var g = function() { ... Scope for g var h = function() { .... Scope for h .... } } } The post-execution lifetimes of inner scopes depend solely on the existence of active external references to their contents. © 2010 David Semeria Page 5
  • 6. Scope Persistence Global Scope var f = function() { var obj = { a: “I live in f's scope”, Scope for f b: “So do I” }; return obj; } External reference to obj from global scope var instance = f(); A scope persists when any of its contents persist. Above, f()'s scope persists given the external reference to obj. A closure is a special case of scope persistence, and is formed when an external reference to a function exists, and that function accesses variables from an outer function's scope. © 2010 David Semeria Page 6
  • 7. Closures Global Scope var f = function() { var x = ... ; Scope for f var g = function() { var y = ... ; Scope for g var h = function() { var z = x + y; Scope for h .... x and y belong to outer scopes } } } External reference to h() from global scope A closure occurs when a function which was not defined in the global scope continues to be referenced (either directly or indirectly) from an object in an active scope after the function that created it has finished execution. In this case, the referenced function's scope, and those surrounding it, persist for as long as any external reference to it exists. © 2010 David Semeria Page 7
  • 8. Closure ? Global Scope var f = function() { alert(“in f”); var g = function() { alert(“in g”); } g(); } f(); //two alerts Is this a closure? © 2010 David Semeria Page 8
  • 9. Closure ? Global Scope var f = function() { alert(“in f”); var g = function() { alert(“in g”); } g(); } f(); //two alerts No; f() runs, it calls g(), then both functions exit. There are no active references from the global scope to g(). © 2010 David Semeria Page 9
  • 10. A Simple Closure Global Scope var f = function(x) { var m = function(y) { return x * y; } return m; } var instance = f(z); We'll see examples of the function working in a moment. First let's look at the references which exist after f() has terminated.... © 2010 David Semeria Page 10
  • 11. A Simple Closure Global Scope var f = function(x) { var m = function(y) { return x * y; } return m; } var instance = f(z); The only external reference we have is instance which points to m(). © 2010 David Semeria Page 11
  • 12. A Simple Closure Global Scope var f = function(x) { var m = function(y) { return x * y; } return m; } var instance = f(z); Within the scope of m() there is a reference to x Given that x is the variable we want to “remember”, we can see that it continues to live because it is indirectly referenced from the global scope. Also, note that x is defined in m()'s parent scope, not m()'s © 2010 David Semeria Page 12
  • 13. A Simple Closure Global Scope var f = function(x) { var m = function(y) { return x * y; } return m; } var myDouble = f(2); var myTreble = f(3); alert (myDouble(10)) // 20 alert (myTreble(10)) // 30 Each time f() runs, a new scope is created in which a new m() is also created. As long as a reference to a given m() exists in the global scope, then m()'s scope, and that of its parent, continue to exist. In this way, each m() has access to the correct value of x . © 2010 David Semeria Page 13
  • 14. A Simple Closure (digging deeper) var f = function(x) { Global Scope var u; var m = function(y) { return x * y; } return m; } What about the variable u ? Does it still exist within f() 's scope, after f() exits ? © 2010 David Semeria Page 14
  • 15. A Simple Closure (digging deeper) var f = function(x) { Global Scope var u; var m = function(y) { return x * y; } return m; } Probably not. But then again, who cares? The garbage collector will see there are no active references to u and will therefore destroy it. When we say that a scope persists we don't imply that all its contents also do. This is the whole point of garbage collection - it destroys only what we have no way of accessing. © 2010 David Semeria Page 15
  • 16. Using Closures ● Information hiding (encapsulation) ● Queued functions (timers) ● Event Handlers ● Callbacks © 2010 David Semeria Page 16
  • 17. Information Hiding Global Scope var f = function(priv,pub) { var a = function() { alert(pub) } // public function var b = function() { c() } // privileged function var c = function() { alert(priv)} // private function return { showPublic : a, showPrivate: b } } var obj = f('PUBLIC','PRIVATE'); obj.showPublic(); // alert: PUBLIC obj.showPrivate(); // alert: PRIVATE The parameter containing the private information priv is only accessible from the outside via the showPrivate() method. Similarly, the private function c() is only accessible via the privileged method b() - which is exposed as showPrivate(). © 2010 David Semeria Page 17
  • 18. Information Hiding (back-tracing) Global Scope var f = function(priv,pub) { var a = function() { alert(pub) } // public function var b = function() { c() } // privileged function var c = function() { alert(priv)} // private function return { showPublic : a, showPrivate: b } } var obj = f('PUBLIC','PRIVATE'); obj.showPublic(); // alert: PUBLIC obj.showPrivate(); // alert: PRIVATE A useful exercise is to work backwards from the external references and check that all the contents we want to persist are either directly or indirectly referenced from an active scope. © 2010 David Semeria Page 18
  • 19. Information Hiding (back-tracing) Global Scope var f = function(priv,pub) { var a = function() { alert(pub) } // public function var b = function() { c() } // privileged function var c = function() { alert(priv)} // private function return { showPublic : a, showPrivate: b } } var obj = f('PUBLIC','PRIVATE'); obj.showPublic(); // alert: PUBLIC obj.showPrivate(); // alert: PRIVATE Let's start with showPublic() © 2010 David Semeria Page 19
  • 20. Information Hiding (back-tracing) Global Scope var f = function(priv,pub) { var a = function() { alert(pub) } // public function var b = function() { c() } // privileged function var c = function() { alert(priv)} // private function return { showPublic : a, showPrivate: b } } var obj = f('PUBLIC','PRIVATE'); obj.showPublic(); // alert: PUBLIC obj.showPrivate(); // alert: PRIVATE We can see that showPublic() references a() © 2010 David Semeria Page 20
  • 21. Information Hiding (back-tracing) Global Scope var f = function(priv,pub) { var a = function() { alert(pub) } // public function var b = function() { c() } // privileged function var c = function() { alert(priv)} // private function return { showPublic : a, showPrivate: b } } var obj = f('PUBLIC','PRIVATE'); obj.showPublic(); // alert: PUBLIC obj.showPrivate(); // alert: PRIVATE And a() references pub © 2010 David Semeria Page 21
  • 22. Information Hiding (back-tracing) Global Scope var f = function(priv,pub) { var a = function() { alert(pub) } // public function var b = function() { c() } // privileged function var c = function() { alert(priv)} // private function return { showPublic : a, showPrivate: b } } var obj = f('PUBLIC','PRIVATE'); obj.showPublic(); // alert: PUBLIC obj.showPrivate(); // alert: PRIVATE And now showPrivate(), which directly references b(). © 2010 David Semeria Page 22
  • 23. Information Hiding (back-tracing) Global Scope var f = function(priv,pub) { var a = function() { alert(pub) } // public function var b = function() { c() } // privileged function var c = function() { alert(priv)} // private function return { showPublic : a, showPrivate: b } } var obj = f('PUBLIC','PRIVATE'); obj.showPublic(); // alert: PUBLIC obj.showPrivate(); // alert: PRIVATE And b() references c() © 2010 David Semeria Page 23
  • 24. Information Hiding (back-tracing) Global Scope var f = function(priv,pub) { var a = function() { alert(pub) } // public function var b = function() { c() } // privileged function var c = function() { alert(priv)} // private function return { showPublic : a, showPrivate: b } } var obj = f('PUBLIC','PRIVATE'); obj.showPublic(); // alert: PUBLIC obj.showPrivate(); // alert: PRIVATE And c() references priv © 2010 David Semeria Page 24
  • 25. Information Hiding (back-tracing) Global Scope var f = function(priv,pub) { var a = function() { alert(pub) } // public function var b = function() { c() } // privileged function var c = function() { alert(priv)} // private function return { showPublic : a, showPrivate: b } } var obj = f('PUBLIC','PRIVATE'); obj.showPublic(); // alert: PUBLIC obj.showPrivate(); // alert: PRIVATE We can see that all the information within the scope of f() (including its parameters) are referenced either directly or indirectly from the global scope. This is actually no surprise, because if a variable was not accessible from the outside, we wouldn't care whether it existed or not. © 2010 David Semeria Page 25
  • 26. Information Hiding (modify once) var f = function(state) { Global Scope var mod = function(newState){ state = newState; mod = function(){} // redefine mod } var get = function(){ return state } return { modify: function(s){mod(s)}, getState: get } } This function uses a closure to store a state variable which can be modified only once. After the first time state is modified, the function which permits its modification (mod) redefines itself (perhaps to return an error). Further details in the next slide... © 2010 David Semeria Page 26
  • 27. Information Hiding (modify once) var f = function(state) { Global Scope var mod = function(newState){ state = newState; mod = function(){} // redefine mod } var get = function(){ return state } return { modify: function(s){mod(s)}, getState: get } } var obj = f('state_1'); alert(obj.getState()) // state_1 obj.modify('state_2'); alert(obj.getState()) // state_2 obj.modify('state_3'); alert(obj.getState()) // state_2 © 2010 David Semeria Page 27
  • 28. Information Hiding (modify once) var f = function(state) { Global Scope var mod = function(newState){ state = newState; mod = function(){} // redefine mod } var get = function(){ return state } return { modify: function(s){mod(s)}, getState: get } } Care must be taken not to leave dangling references to objects we wish to remove / redefine. This is the reason why mod is exposed as function(s){mod(s)} and not as a simple reference to mod. © 2010 David Semeria Page 28
  • 29. Information Hiding (modify once) var f = function(state) { Global Scope var mod = function(newState){ state = newState; mod = function(){} // redefine mod } var get = function(){ return state } return { modify: function(s){mod(s)}, getState: get } } After mod is redefined, there are no longer any references to the original function, and so its is destroyed by the garbage collector. Hence, this pattern is also useful for removing module initialization code once it has run. © 2010 David Semeria Page 29
  • 30. Using Closures ● Information hiding (encapsulation) ● Queued functions (timers) ● Event Handlers ● Callbacks © 2010 David Semeria Page 30
  • 31. Queued Functions var fade = function(e,delay) { Global Scope e.style.opacity = 1; var proc = function() { if (e.style.opacity >= 0.1) { e.style.opacity -= 0.1; setTimeout(proc,delay); } else { e.style.opacity = 0; } } proc(); } The key point here is that the garbage collector (for very good reasons) considers queued functions (inserted via setTimeout and setInterval) to be part of the global scope. © 2010 David Semeria Page 31
  • 32. Queued Functions var fade = function(e,delay) { Global Scope e.style.opacity = 1; var proc = function() { if (e.style.opacity >= 0.1) { e.style.opacity -= 0.1; setTimeout(proc,delay); } else { e.style.opacity = 0; } } proc(); } This means that the variables we need to remember (e and delay) continue to exist within fade() and proc()'s scopes given the queued reference to proc(). © 2010 David Semeria Page 32
  • 33. Using Closures ● Information hiding (encapsulation) ● Queued functions (timers) ● Event Handlers ● Callbacks © 2010 David Semeria Page 33
  • 34. Event Handlers ( good example ) var attach = function(e) { Global Scope for (var i=0; i < e.ChildNodes.length; i++) { e.ChildNodes.length[i].onclick = function(x){ return function(evt){ alert(x+1) } }(i); } } [1] [2] [3] This function takes an element and attaches to each of its child nodes an onclick function which alerts its position in the list (starting from 1). To demonstrate the function working, we create three spans ([1],[2],[3]) and pass their parent node to attach(), as shown next.. © 2010 David Semeria Page 34
  • 35. Event Handlers ( good example ) var attach = function(e) { Global Scope for (var i=0; i < e.ChildNodes.length; i++) { e.ChildNodes.length[i].onclick = function(x){ return function(evt){ alert(x+1) } }(i); } } [1] [2] [3] Onclick alert 1 © 2010 David Semeria Page 35
  • 36. Event Handlers ( good example ) var attach = function(e) { Global Scope for (var i=0; i < e.ChildNodes.length; i++) { e.ChildNodes.length[i].onclick = function(x){ return function(evt){ alert(x+1) } }(i); } } [1] [2] [3] Onclick alert 3 © 2010 David Semeria Page 36
  • 37. Event Handlers ( good example ) var attach = function(e) { Global Scope for (var i=0; i < e.ChildNodes.length; i++) { e.ChildNodes.length[i].onclick = function(x){ return function(evt){ alert(x+1) } }(i); } } [1] [2] [3] This example is often used to show applications of closures in event handlers (we'll see how it works in a moment). In reality, it's not a closure - because the inner function contains no references to the scope of the outer one. The above function actually provides a workaround for an unwanted closure created by an incorrect implementation, which is shown next. © 2010 David Semeria Page 37
  • 38. Event Handlers ( bad example ) var attach = function(e) { Global Scope for (var i=0; i < e.ChildNodes.length; i++) { e.ChildNodes.length[i].onclick = function(){ alert(i+1) } } } [1] [2] [3] Onclick alert 4 © 2010 David Semeria Page 38
  • 39. Event Handlers ( bad example ) var attach = function(e) { Global Scope for (var i=0; i < e.ChildNodes.length; i++) { e.ChildNodes.length[i].onclick = function(){ alert(i+1) } } } [1] [2] [3] Onclick alert 4 © 2010 David Semeria Page 39
  • 40. Event Handlers ( bad example ) var attach = function(e) { Global Scope for (var i=0; i < e.ChildNodes.length; i++) { e.ChildNodes.length[i].onclick = function(){ alert(i+1) } } } [1] [2] [3] Onclick alert 4 © 2010 David Semeria Page 40
  • 41. Event Handlers ( bad example ) var attach = function(e) { Global Scope for (var i=0; i < e.ChildNodes.length; i++) { e.ChildNodes.length[i].onclick = function(){ alert(i+1) } } } [1] [2] [3] This incorrect behaviour stems from the accidental creation of a closure. The i used in each onclick function is a reference to the same i used in the for loop. This reference creates the unwanted closure, meaning the for loop's i continues to live on (with the value which caused the loop to terminate) in each of the created onclick functions. © 2010 David Semeria Page 41
  • 42. Event Handlers ( good example ) var attach = function(e) { Global Scope for (var i=0; i < e.ChildNodes.length; i++) { e.ChildNodes.length[i].onclick = function(x){ return function(evt){ alert(x+1) } }(i); } } [1] [2] [3] Back to the correct version. The unwanted effects of the closure are avoided by passing i to a function which is invoked immediately (note the (i) at the end). This means that the x used in the new onclick's function is bound to the correct value of i each time. And if we really want to use a closure, we can use the example which follows. © 2010 David Semeria Page 42
  • 43. Event Handlers ( using a closure ) var attach = function(e) { Global Scope for (var i=0; i < e.ChildNodes.length; i++) { var msg = 'hello from '; e.ChildNodes.length[i].onclick = function(x){ return function(evt){ alert(msg+(x+1)) } }(i); } return function (m) {msg = m}; } var mod = attach(elem); [1] [2] [3] Onclick alert hello from 3 © 2010 David Semeria Page 43
  • 44. Event Handlers ( using a closure ) var attach = function(e) { Global Scope for (var i=0; i < e.ChildNodes.length; i++) { var msg = 'hello from '; e.ChildNodes.length[i].onclick = function(x){ return function(evt){ alert(msg+(x+1)) } }(i); } return function (m) {msg = m}; } var mod = attach(elem); [1] [2] [3] Here, we create a variable - msg - within the scope of attach() . We then create a closure by referencing msg directly in the onclick function. © 2010 David Semeria Page 44
  • 45. Event Handlers ( using a closure ) var attach = function(e) { Global Scope for (var i=0; i < e.ChildNodes.length; i++) { var msg = 'hello from '; e.ChildNodes.length[i].onclick = function(x){ return function(evt){ alert(msg+(x+1)) } }(i); } return function (m) {msg = m}; } var mod = attach(elem); [1] [2] [3] By returning an access function for msg, we can change it's value from the outside... © 2010 David Semeria Page 45
  • 46. Event Handlers ( using a closure ) var attach = function(e) { Global Scope for (var i=0; i < e.ChildNodes.length; i++) { var msg = 'hello from '; e.ChildNodes.length[i].onclick = function(x){ return function(evt){ alert(msg+(x+1)) } }(i); } return function (m) {msg = m}; } var mod = attach(elem); mod('goodbye from '); [1] [2] [3] Onclick alert goodbye from 3 © 2010 David Semeria Page 46
  • 47. Using Closures ● Information hiding (encapsulation) ● Queued functions (timers) ● Event Handlers ● Callbacks © 2010 David Semeria Page 47
  • 48. Callbacks Global Scope var replace = function(e,ref) { var callback = function(t) { e.innerHTML = t; } makeXHR(callback,ref); // async } replace(elem,ref); © 2010 David Semeria Page 48
  • 49. Callbacks Global Scope var replace = function(e,ref) { var callback = function(t) { e.innerHTML = t; } makeXHR(callback,ref); // async } External reference to callback from global scope replace(elem,ref); By now it should be clear what is happening. The makeXHR() call returns immediately and maintains a reference to callback(), which in turn retains a reference to e. Even though replace() exits immediately, the contents of the closure will persist until they are needed by the callback() function. © 2010 David Semeria Page 49
  • 50. Callbacks Global Scope var replace = function(e,ref) { var callback = function(t) { e.innerHTML = t; } makeXHR(callback,ref); // async } replace(elem,ref); This simple example demonstrates the power of closures. Even though we pass one simple reference to makeXHR(), we can elegantly store as much context-specific information as we like in the closure, safe in the knowledge that it will available when we need it. © 2010 David Semeria Page 50
  • 51. This & That ( overview ) ● Many discussions of closures frequently get bogged down with issues regarding the self-referential object pointer this. Consequently, we have avoided it so far. ● Under certain circumstances, the use of this can lead to unintended behaviour - examples of which are provided in the following sides. ● That said, sometimes this can be very useful (if not essential), and so we'll also present a few examples of how this can usefully be employed in closures. © 2010 David Semeria Page 51
  • 52. This & That ( problems with 'this' ) Global Scope var F = function() { this.x = 1; if (this === window) alert(“'this' is bound to window”); if (this.x) alert(“'this' is bound to new object”); } var obj = new F(); // alert: 'this' is bound to new object var obj = F(); // alert: 'this' is bound to window ● It is important to bear in mind that this is only bound to the object being created by a constructor function when new is used. © 2010 David Semeria Page 52
  • 53. This & That ( object construction ) Global Scope var f = function(a,b,c) { var obj = new function(){ this.a = a; this.b = b; } return { d: obj, c: c } } var newObj = f('a','b','c'); ● For this reason it is often better to enclose new within a general function, which no longer needs to be called using new. Also, objects can easily be created without using new, for example by using object literals {} ● The contrived example above employs both these techniques © 2010 David Semeria Page 53
  • 54. This & That ( when 'this' is useful ) Global Scope var myOnclick = function(evt) { alert(“my id is “ + this.id); } elem.onclick = myOnclick; Sometimes, however, the use of this is either unavoidable or useful (or both). Examples are situations when functions are attached to objects, such as event handlers. But it is important to remember that each function may access a different this – a situation that can lead to unexpected behaviour, as shown next. © 2010 David Semeria Page 54
  • 55. This & That ( bad example ) var multiply = function(list){ Global Scope for (var i=0; i<list.length; i++) { list[i].onclick = function(e){ var newE = document.createElement('div'); this.appendChild(newE); newE.onclick = function(e){ alert('created by '+this.id); //this is wrong } } } elem.onclick = multiply(lst); // lst references 3 blue divs The purpose of this function is to take an array of elements (list) and assign a new onlclick function to each. The onclick function will create a new div within the clicked element and assign to the new div an onclick function which will alert the id of the element which created it. © 2010 David Semeria Page 55
  • 56. This & That ( bad example ) var multiply = function(list){ Global Scope for (var i=0; i<list.length; i++) { list[i].onclick = function(e){ var newE = document.createElement('div'); this.appendChild(newE); newE.onclick = function(e){ alert('created by '+this.id); //this is wrong } } } elem.onclick = multiply(lst); // lst references 3 blue divs Unfortunately, the function does not work as hoped. In the following slide, we create three blue divs and pass them (as the members of list) to the above function. We assume the rightmost blue div has already been clicked, thus creating a new (red) div - which does not report its creator correctly. © 2010 David Semeria Page 56
  • 57. This & That ( bad example ) var multiply = function(list){ Global Scope for (var i=0; i<list.length; i++) { list[i].onclick = function(e){ var newE = document.createElement('div'); this.appendChild(newE); newE.onclick = function(e){ alert('created by '+this.id); //this is wrong } } Onclick alert } Created by undefined elem.onclick = multiply(lst); // lst references 3 blue divs id: blue_div_1 id: blue_div_2 id: blue_div_3 © 2010 David Semeria Page 57
  • 58. This & That ( bad example ) var multiply = function(list){ Global Scope for (var i=0; i<list.length; i++) { list[i].onclick = function(e){ var newE = document.createElement('div'); this.appendChild(newE); newE.onclick = function(e){ alert('created by '+this.id); //this is wrong } } } elem.onclick = multiply(lst); // lst references 3 blue divs The reason for the incorrect behaviour stems from the fact that the innermost onclick function also creates its own this – thereby obscuring the this we were hoping to use from the outer scope. © 2010 David Semeria Page 58
  • 59. This & That ( good example ) var multiply = function(list){ Global Scope for (var i=0; i<list.length; i++) { list[i].onclick = function(e){ var newE = document.createElement('div'); this.appendChild(newE); var that = this; newE.onclick = function(e){ alert('created by '+that.id); //this is right } } } elem.onclick = multiply(lst); // lst references 3 blue divs Fortunately, the solution is straightforward. We merely create a reference to the outer this – by convention named that – which will be available to the inner scope. © 2010 David Semeria Page 59
  • 60. This & That ( good example ) var multiply = function(list){ Global Scope for (var i=0; i<list.length; i++) { list[i].onclick = function(e){ var newE = document.createElement('div'); this.appendChild(newE); var that = this; newE.onclick = function(e){ alert('created by '+that.id); //this is right } } Onclick alert } Created by blue_div_3 elem.onclick = multiply(lst); // lst references 3 blue divs id: blue_div_1 id: blue_div_2 id: blue_div_3 © 2010 David Semeria Page 60
  • 61. Comments are welcome, please leave them here David Semeria http://lmframework.com February 2010 http://twitter.com/hymanroth © 2010 David Semeria Page 61