MollyPages.org
"You were wrong case. To live here is to live." |
Pages /
Database /
Forms /
Servlet /
Javadocs
/
License & Download /
Tutorials /
Cookbook
/
Contact & Hire
|
function foo() { function bar() { } }
function bar
is not reachable from outside
function foo
by saying something like: foo.bar()
function foo() { bar = function () { } } foo(); //=> assigns the inner (anonymous) function to global variable bar bar(); //=>invokes nested function bar
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).
Note, foo()
has to be invoked at least
once for global variable bar
to be set.
function foo() { var bar = function () { } return bar; } var bar_reference = foo(); bar_reference(); //=>invokes nested function bar
function bar
is now reachable from outside
function foo
, since it has been assigned
to a global variable bar_reference
.
Note, foo()
has to be invoked at least
once for it to return the reference to bar
.
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"
bar_ref()
correctly alerts "hello" in this example
because the variable x is still accessible via the closure.
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();
bar_ref()
correctly alerts "hello" in this example
because the local method parameter variable is accessible
via the closure.
function foo(x) { function bar() { alert(x); } return bar; } var bar_ref_1 = foo("hello"); var bar_ref_2 = foo("world"); bar_ref_1(); //alerts hello bar_ref_2(); //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.
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).
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:
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
).
setTimeout
only accepts a function.
SomeElement.addEventHandler("onclick",In this case, it's imposssible to access function specific data (unless either a global variable or a closure is used).obj1, some_function);
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 unfortunately necessary for the above reasons).
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).