"Csaba Gabor" <danswer@gmail.com> writes:
[color=blue]
> var foo = function (baz) {
> var res = typeof(frob)=="undefined" ? "" : frob;
> alert ("frob: " + typeof(frob) + " " + res); }
>
> function bar() {
> return function(baz,frob) {
> return function(baz) {[/color]
....[color=blue]
> foo(); }} (7, "input");
> }
>
> bar()();
>
>
> However, if I now take 'foo' in function bar and replace it with foo's
> definition surround by parens (see below), then the third alert will
> show me that frob has value "input". My question is: why is there a
> difference,[/color]
That's what closures are all about. When a closure is contained, the
scope chain at that point is stored as part of it, and it is reinserted
when the function is called.
When replacing the "foo" identifier, bound to a closure created in the
global scope, with a function expression, then that function expression
creates a closure containing the scope at that point. That scope contains
the variables frob and baz, which the global scope didn't.
[color=blue]
> and how is it possible to wrap a named function foo
> (external to bar, without frob as a named parameter) so that it can be
> called within bar and know about non global frob.[/color]
You can't. The scope chain used to resolve variables in the body
of a function is created when the function expression/declaration
is evaluated to a closure. If you want values to be available to
that function body, you must pass them as parameters or store them
in variables that are part of the scope captured in the closure.
[color=blue]
> return function(baz,frob) {
> return function(baz) {[/color]
....[color=blue]
> (function (baz) {
> var res = typeof(frob)=="undefined" ? "" : frob;[/color]
(in scope of "frob" paremeter declared above).
[color=blue]
> alert ("frob: " + typeof(frob) + " " + res); })();
> }} (7, "input");
> }[/color]
The word "closure" means more than just "first class function".
Lisp has first class functions but dynamic scope, so a function
declaration with a free variable (one not bound by the expression
itself) will use the scope available at the call point, not the
declaration point.
Example (from Emacs Lisp):
(defun foo (x) (lambda (y) (+ x y))) ;; x free var in (lambda (y) (+ x y))
(let ((foo2 (foo 2))
(x 37))
(funcall foo2 4)) ;; yields 41, value of x not kept
Compare that to javascript
:
function foo(x) { return function(y) { return x + y; }; }
var foo2 = foo(2);
var x = 37;
alert(foo2(4)); // alerts 6, value of x stored in closure
Ditto for Scheme, a dialect of Lisp with static scope:
(defun foo (x) (lambda (y) (+ x y)))
(let ((foo2 (foo 2))
(x 37))
(foo2 4)) ;; yields 6 too.
The closure contains not only the body of the function, but also the
the free variables. And it's not just their values, it's the actual
variables, as a common error shows:
for (var i = 0; i < 10; i++) {
elems[i].onclick = function() {
alert("You clicked element number " + i);
};
}
Many would expect the above to add ten onclick handlers which each
alert a different number between 0 and 9.
However, no matter which element is clicked, you are told that you
clicked element number 10. That's because all ten closures[1] will
have the same scope chain stored in them, and in that scope chain
is one variable called "i" with the value "10" (after the loop is
done).
The way to avoid this problem is to create ten functions with
either different bodies or different scope chains. Different bodies
would suggest the Function constructor, and managing source code
at runtime is rarely a good idea. The way to create different scope
chains is to make new function calls:
elems[i].onclick = (function (elemNr) {
return function() {
alert("You clicked element number " + elemNr);
};
})(i);
or using the "with" operator:
with ( {elemNr : i} ) {
elems[i].onclick = function() {
alert("You clicked element number " + elemNr);
};
}
/L
[1] Actually, implementations may choose to create only one closure
here, since it knows the ten closures will be indistinguishable (same
body, same scope chain).
--
Lasse Reichstein Nielsen -
lrn@hotpop.com
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
'Faith without judgement merely degrades the spirit divine.'