Você está na página 1de 9

http://www.mollypages.org/misc/jsclo.mp Table of contents 1. 2. 3. 4. 5. Good/reference articles Preliminary note: Nested functions Closures When are closures useful ?

Browser specific hacks and bugs

1. Good/reference articles First, read the following links:


http://blog.morrisjohns.com/javascript_closures_for_dummies.html http://www.hunlock.com/blogs/Closing_The_Book_On_Javascript_ Closures http://www.jibbering.com/faq/faq_notes/closures.html http://laurens.vd.oever.nl/weblog/items2005/closures/ http://www.codeproject.com/KB/scripting/leakpatterns.aspx http://msdn.microsoft.com/en-us/library/bb250448(VS.85).aspx http://blogs.msdn.com/ericlippert/archive/2003/09/17/53028.aspx

2. Preliminary note: Nested functions

A nested function is defined within another function.


function foo() { function bar() { } }

is not reachable from outside function something like: foo.bar()


function bar

foo

by saying

Inner functions can be exposed outside the outer function.


function foo() { bar = function () { } }

foo(); //=> assigns the inner (anonymous) function to global variable bar bar(); //=>invokes nested function bar

is now reachable from outside function foo, since it has been assigned to a global variable bar (since bar is not preceded with "var" it is a global variable).
function bar

Note, foo() has to be invoked at least once for global variable bar to be set.

Inner functions can also be returned from the outer function


function foo() { var bar = function () { return bar; } }

var bar_reference = foo(); bar_reference(); //=>invokes nested function bar foo,

is now reachable from outside function been assigned to a global variable bar_reference.
function bar

since it has

Note, foo() has to be invoked at least once for it to return the reference to bar. 3. Closures

As shown above, nested functions can be accessed from outside the outer function. A closure is created the moment a nested function isdefined and returned (or defined and invoked). A closure simply means that the inner function has access to internal (local) variables of the outer function. The interesting part is that it has access to those local variables even after the outer function has finished executing and no longer exists. This is achieved by Javascript internally saving the state of the local outer variables in a separate "closure" object.

function foo() {

var x = "hello"; function bar() { alert(x); } return bar; } var bar_ref = foo(); //foo has finished executing at this point and local //variable x does not exist anymore bar_ref(); //=> alerts "hello"

correctly alerts "hello" in this example because the variable x is still accessible via the closure.
bar_ref()

Local variables saved in closures are anything declared via the "var" keyword in the outer function and any method parameters of the outer function.
function foo(x) { function bar() { alert(x); } return bar; } var bar_ref = foo("hello"); bar_ref();

correctly alerts "hello" in this example because the local method parameter variable is accessible via the closure.
bar_ref()

Important note: A new closure is created everytime a inner function is assigned or returned.

function foo(x) { function bar() { alert(x); } return bar; } var bar_ref_1 = foo("hello"); var bar_ref_2 = foo("world"); bar_ref_1(); bar_ref_2(); //alerts hello //alerts world

Note: this means that multiple closures are created by the same nested function (every time the nested function is called) and modifying/changing variables contained in one closure cannot affect any other closures.

4. When are closures useful ?

Closures essentially save us from the need to create global variables. They are mostly useful in event handlers/callback functions, when such functions need to access some callback specfic data. In Java, we can specify a method as a so-called "callback" function. We can create several different objects (each with different instance data) and can specify a particular object upon which the callback

method will be invoked. This method, when invoked, will have access to its own object data.
class MyObject { String greeting; MyObject(String word) { this.greeting = word; } void showGreeting() { System.out.print(greeting); } } MyObject obj1 = new MyObject("hello"); MyObject obj2 = new MyObject("world"); SomeEventHandler.addCallBack(obj1); //invokes showGreeting() and prints "hello" SomeEventHandler2.addCallBack(obj2); //invokes showGreeting() and prints "world"

Note, the event handler runs on a different thread (typically awt/swing event handler thread) but the method that is invoked has access to it's own object data (since all methods are tied to objects). Alternately, (in Java) it's also common to pass a "this" object pointer to an event handler (when the event handler is added by the object on itself) and the event handler can use the "this" pointer to access that particular object's data. The object passed to the event handler typically implements a interface, such that the event hander (and the compiler) know that the object has a well known method that will be called back. (showGreeting() in this example). By contrast, in Javascript (back in the day, things started out in this fashion:
var greeting = "hello"; function foo() { alert(greeting); } SomeElement.addEventHandler("onclick", foo); SomeElement2.addEventHandler("onclick", foo);

There were event handlers that would invoke global functions (which could access only global variables). In this example, we are limited to only one value for the greeting variable, every time it is read from foo() (since it is a global variable). If we now try and follow the Java model and object-ify things, we can say:

function MyObject(word) { this.greeting = word; this.handle = function() { alert(this.greeting); } } var obj1 = new MyObject("hello"); var obj2 = new MyObject("world"); SomeElement.addEventHandler("onclick", obj1); SomeElement2.addEventHandler("onclick", obj2);

This may or may not work. It depends on how the callback mechanism is coded: 1. The callback mechanism could invoke the callback method as a function. If instead of calling obj1.handle(), the event handler calls handle(), then 'this' will not point to the right object (it will point to the global object, not obj1). 2. The callback mechanism may not even accept an object, it may only accept a function. This is very common in Javascript. For example, setTimeout only accepts a function.
3. SomeElement.addEventHandler("onclick", obj1, some_function);

In this case, it's imposssible to access function specific data (unless either a global variable or a closure is used). So how do closures solve this problem ?
function remember_me(word) { function inner() { alert(word); } return inner; } SomeElement.addEventHandler("onclick", remember_me("hello"));

In this example, the function and associated data ("hello") is remembered together, in the form of a closure. The event handler takes a function to a "callback" function (not an object). Yet, the function has access to it's own separate data and each closure-function has independent separate data. This is conceptually similar to having an method based callback, where the method has access to it's object data.

I feel that closures tend to make things a whole lot more complicated and hard to understand in general. They are not really a feature in a programming language, just a source of confusion (and in Javascript, sometimes necessary for the above reasons).
5. Browser specific hacks and bugs

Internet explorer has some garbage collection bugs. These are not closure-specific but are easily exposed when when using closures.

As shown in the above diagram, IE (version 7 and earlier) has separate garbage collectors for native Javascript and HTML DOM that is reflected as Javascript objects. If there is a circular link between a HTML DOM object and a native object, then neither is garbage colleted for the duration of the IE browser session (even if the user navigates away from the page).

function addHandler() { var mydiv = document.getElementById("myid"); mydiv.onclick = function() { alert(this.innerHTML + "\n"); }

In this example, mydiv (in the HTML DOM side) refers to native JS code via its onclick event handler. The JS code for the event handler, refers back to mydiv via the inadvertent closure formed by the inner function. This circular reference leaks memory in IE. One way around this is to say:
function addHandler() { var mydiv = document.getElementById("myid"); mydiv.onclick = handler; } function handler() { alert(this.innerHTML + "\n"); }

Since the handler function is not a nested function, no closure is formed. Another way to do this without a closure is:
function addHandler() { var mydiv = document.getElementById("myid");

mydiv.onclick = new Function( 'alert(this.innerHTML)' ); }

Even though the handler function is defined inline, the use of the Function constructor prevents a implicit closure (functions created via a Function constructor do not save local scope). Return to Misc. topics index

Você também pode gostar