By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
432,101 Members | 1,426 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 432,101 IT Pros & Developers. It's quick & easy.

JavaScript, Higher Order Functions, Closures, how come?

P: n/a

Take a look at the following snippet:

<html>
<head>
<script>
function add(elementId) {
var container = document.getElementById(elementId);
for (var i = 0; i < 10; i++) {
var elt = document.createElement('div');
elt.innerHTML = "" + i;

// Beware!
elt.onclick = function () {window.alert("" + i);};
container.appendChild(elt);
}
}
</script>

</head>

<body>
<div id=myDiv onClick=add('myDiv')>
Here's my DIV
</div>
</body>
</html>

Now, when you click on the DIV element it produces and adds 10
DIV elements. That's okay but when you click on the child DIVs,
alert gives you 10. Not that you click on the first one and
you get 0 and the next one and 1, etc.

Why does that anonymous function always show the value of 10?

I'm very surprised at this output!

Any ideas, explanations, tips?

--
Emre Sevinc

eMBA Software Developer Actively engaged in:
http:www.bilgi.edu.tr http://ileriseviye.org
http://www.bilgi.edu.tr http://fazlamesai.net
Cognitive Science Student http://cazci.com
http://www.cogsci.boun.edu.tr
Feb 14 '06 #1
Share this Question
Share on Google+
10 Replies


P: n/a
Emre Sevinc wrote:
<html>
<head>
<script>
function add(elementId) {
var container = document.getElementById(elementId);
for (var i = 0; i < 10; i++) {
var elt = document.createElement('div');
elt.innerHTML = "" + i;

// Beware!
elt.onclick = function () {window.alert("" + i);};
container.appendChild(elt);
}
}
</script>

</head>

<body>
<div id=myDiv onClick=add('myDiv')>
Here's my DIV
</div>
</body>
</html>

Now, when you click on the DIV element it produces and adds 10
DIV elements. That's okay but when you click on the child DIVs,
alert gives you 10. Not that you click on the first one and
you get 0 and the next one and 1, etc.

Why does that anonymous function always show the value of 10?

The alert shows you the value of 'i' at the time when alert is called. i.e.
by the time you click on the div the loop has finished running and 'i' is
10.

When you access a variable from an outer scope inside a function it always
acts on the current value of the variable. You seem to have expected it to
make a copy of the variable's value when the function was created, but it
won't do that.

If you want to use the value at the time the function is created you have
to save that value in another variable. One way to do this is to create the
function inside another factory function:

function new_alert(value) {
return function () {window.alert(value);};
};

...
elt.onclick = new_alert(i);

This way the parameter of the factory function remains unchanged even
though i is incremented so you see the value you expected.
Feb 14 '06 #2

P: n/a
>>>>> "DB" == Duncan Booth <du**********@invalid.invalid> writes:

DB> Emre Sevinc wrote:
<html> <head> <script> function add(elementId) { var container
= document.getElementById(elementId); for (var i = 0; i < 10;
[...]
Why does that anonymous function always show the value of 10?

DB> The alert shows you the value of 'i' at the time when alert is
DB> called. i.e. by the time you click on the div the loop has
DB> finished running and 'i' is 10.

That's what still confuses me. I'm generating a function on-the-fly
and assigning it to a new DIV element's onClick attribute and
this is done before the loop ends. Right? If that is right, I mean
that generated functions must be taking values 0, 1, ... 9. I think
this is the crucial point and which confuses me. What kind of an order
of execution is this?

DB> When you access a variable from an outer scope inside a
DB> function it always acts on the current value of the
DB> variable. You seem to have expected it to make a copy of the
DB> variable's value when the function was created, but it won't
DB> do that.

Yes, you're right. I expected it to build a function with the
values I have provided = 0, 1, ..., 9 (and I don't understand
why it didn't).

DB> If you want to use the value at the time the function is
DB> created you have to save that value in another variable. One
DB> way to do this is to create the function inside another
DB> factory function:

DB> function new_alert(value) { return function ()
DB> {window.alert(value);}; };

DB> ... elt.onclick = new_alert(i);

DB> This way the parameter of the factory function remains
DB> unchanged even though i is incremented so you see the value
DB> you expected.

Yes, it works now, but I'm still somehow baffled at this situation
(my mind is swinging between Lisp and JavaScript! :))

Thank you very much for the explanations.
--
Emre Sevinc

eMBA Software Developer Actively engaged in:
http:www.bilgi.edu.tr http://ileriseviye.org
http://www.bilgi.edu.tr http://fazlamesai.net
Cognitive Science Student http://cazci.com
http://www.cogsci.boun.edu.tr
Feb 14 '06 #3

P: n/a


Emre Sevinc wrote:

function add(elementId) {
var container = document.getElementById(elementId);
for (var i = 0; i < 10; i++) {
var elt = document.createElement('div');
elt.innerHTML = "" + i;

// Beware!
elt.onclick = function () {window.alert("" + i);};
container.appendChild(elt);
}
} Now, when you click on the DIV element it produces and adds 10
DIV elements. That's okay but when you click on the child DIVs,
alert gives you 10. Not that you click on the first one and
you get 0 and the next one and 1, etc.

Why does that anonymous function always show the value of 10?


Well your subject names the reason, closures. (There are no higher order
functions (functions taking functions as arguments) however, not sure
why your subject names them too).

As for the closure, those anynymous functions you defined as the onclick
event handler are inner functions of the function add in which the
variable i is declared. When add is called and executed, that variable i
is incremented from 0 to 10 and the last value is what is available in
the closure to those inner functions as they are executed after the add
call is finished. If you executed an onclick handler during the add call
then of course the current value of i is what is accessed e.g.

function add() {
var container = document.body;
for (var i = 0; i < 10; i++) {
var elt = document.createElement('div');
elt.appendChild(document.createTextNode(i));
elt.onclick = function () {window.alert("" + i);};
container.appendChild(elt);
if (i == 5) {
elt.onclick();
}
}
}
add();

And i can be changed by those inner functions during the execution of
add and after add has been finished:

function add() {
var container = document.body;
for (var i = 0; i < 10; i++) {
var elt = document.createElement('div');
elt.appendChild(document.createTextNode(i));
elt.onclick = function () { alert(++i); };
container.appendChild(elt);
if (i == 5) {
elt.onclick();
}
}
}
add();


Check the FAQ notes on closures:
<http://www.jibbering.com/faq/faq_notes/closures.html>

If you want to have an onclick handler that uses the i value that i has
during the creation of the event handler then a function constructed
with new Function e.g.
elt.onclick = new Function("evt", "alert(" + i + ");");
is one way.
--

Martin Honnen
http://JavaScript.FAQTs.com/
Feb 14 '06 #4

P: n/a
On 14/02/2006 13:41, Emre Sevinc wrote:

[snip]
<script>
The type attribute is required:

<script type="text/javascript">
function add(elementId) {
var container = document.getElementById(elementId);
for (var i = 0; i < 10; i++) {
var elt = document.createElement('div');
elt.innerHTML = "" + i;
Though I personally prefer to use the String function to perform string
conversions, it is not necessary here; the conversion is implicit.
// Beware!
Indeed...
elt.onclick = function () {window.alert("" + i);};
....this closure will form a circular reference involving DOM nodes
(container and elt). In IE, this will lead to a memory leak[1]. The loop
can be broken by assigning null before the add function returns.

[snip]
<div id=myDiv onClick=add('myDiv')>
That onclick attribute value must be quoted:

<div id=myDiv onclick="add('myDiv');">

However, I would advise that you quote /all/ attribute values.

[snip]
Why does that anonymous function always show the value of 10?
The values of variables in the scope chain of a function are not frozen.
They can be changed during execution. Each time the function expression
is evaluated, a new execution context is created, but each of these
contexts share the four local variables present in the scope of the add
function: elementId, container, i, and elt. As the loop continues
modifies the latter two, each created function object will see these
modifications.

[snip]
Any ideas, explanations, tips?


You could either use the Function constructor, or create a separate
function to perform the listener assignment.

function add(id) {
var container = document.getElementById(id);

for(var i = 0; i < 10; ++i) {
var element = document.createElement('div');

element.appendChild(document.createTextNode(i));
element.onclick = new Function('alert(\'' + i + '\');');

container.appendChild(element);
}
container = element
= null;
}

Or replacing the onclick assignment with:

element.onclick = createListener(i);

removing the null assignment (it's not necessary, here), and adding:

function createListener(value) {
return function() {
alert(value);
};
}
For clarity, feature detection was omitted from the code above. It
should be included in production code.

Hope that helps,
Mike
[1] <http://www.jibbering.com/faq/faq_notes/closures.html#clMem>

--
Michael Winter
Prefix subject with [News] before replying by e-mail.
Feb 14 '06 #5

P: n/a
Emre Sevinc wrote:
>> "DB" == Duncan Booth <du**********@invalid.invalid> writes:
DB> Emre Sevinc wrote: >> <html> <head> <script> function add(elementId) { var container
>> = document.getElementById(elementId); for (var i = 0; i < 10;
>>[...]
>> Why does that anonymous function always show the value of 10?
>>

DB> The alert shows you the value of 'i' at the time when alert is
DB> called. i.e. by the time you click on the div the loop has
DB> finished running and 'i' is 10.

That's what still confuses me. I'm generating a function on-the-fly
and assigning it to a new DIV element's onClick attribute and
this is done before the loop ends. Right? If that is right, I mean
that generated functions must be taking values 0, 1, ... 9. I think
this is the crucial point and which confuses me. What kind of an order
of execution is this?


Its a completely logical order of execution. Functions execute when they
are called and not before.

You are generating a function on-the-fly and assigning the function to an
onclick attribute, but you aren't calling the function until the div is
clicked.

It doesn't matter where in the block the function is defined: the code
inside the function is executed when it is called, NOT when it is defined.

Your function:

function add(elementId) {
var container = document.getElementById(elementId);
for (var i = 0; i < 10; i++) {
var elt = document.createElement('div');
elt.innerHTML = "" + i;

// Beware!
elt.onclick = function () {window.alert("" + i);};
container.appendChild(elt);
}
}

could equally well be written with the function outside the loop:

function add(elementId) {
var callme = function() { window.alert("" + i);};

var container = document.getElementById(elementId);
for (var i = 0; i < 10; i++) {
var elt = document.createElement('div');
elt.innerHTML = "" + i;

// Beware!
elt.onclick = callme;
container.appendChild(elt);
}
}
If you still aren't clear then consider:

function test() {
var container = [];
for (var i = 0; i < 10; i++) {
container.push(function() { alert(i); });
container[0]();
}
container[9]();
i = 42;
container[9]();
}
test();

Its the same thing as you were doing: save the function then call it later.
Every time you call the function it shows you the current value of 'i'.
Outside the loop it shows you the final value of i, but you can reassign it
to whatever you want.
Feb 14 '06 #6

P: n/a

Hello Emre,
Emre Sevinc <em***@bilgi.edu.tr> writes:

Yes, it works now, but I'm still somehow baffled at this situation
[...]
(my mind is swinging between Lisp and JavaScript! :))


It's absolutely the same in Lisp; check this out:

-------------------------------
aundro@paddy:~$ sbcl
This is SBCL 0.9.6, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
* (defvar *foo* nil)

*FOO*
* (dotimes (i 10)
(push (lambda () (format t "~s~%" i)) *foo*))

NIL
* *foo*

(#<CLOSURE (LAMBDA #) {906C4DD}> #<CLOSURE (LAMBDA #) {906C4C5}>
#<CLOSURE (LAMBDA #) {906C4AD}> #<CLOSURE (LAMBDA #) {906C495}>
#<CLOSURE (LAMBDA #) {906C47D}> #<CLOSURE (LAMBDA #) {906C465}>
#<CLOSURE (LAMBDA #) {906C44D}> #<CLOSURE (LAMBDA #) {906C435}>
#<CLOSURE (LAMBDA #) {906C41D}> #<CLOSURE (LAMBDA #) {906C405}>)
* (funcall (nth 5 *foo*))
10
NIL
* (funcall (nth 1 *foo*))
10
NIL
*
-------------------------------

Best regards,

Arnaud
Feb 14 '06 #7

P: n/a
Michael Winter wrote:
On 14/02/2006 13:41, Emre Sevinc wrote:
function add(elementId) {
var container = document.getElementById(elementId);
for (var i = 0; i < 10; i++) {
var elt = document.createElement('div');
elt.innerHTML = "" + i;

[...]
// Beware!


Indeed...
elt.onclick = function () {window.alert("" + i);};


...this closure will form a circular reference involving DOM nodes
(container and elt). In IE, this will lead to a memory leak[1]. The loop
can be broken by assigning null before the add function returns.


Could you please elaborate what the circular reference would be here?
Because I do not see it (yet).
PointedEars
Feb 14 '06 #8

P: n/a
On 14/02/2006 17:51, Thomas 'PointedEars' Lahn wrote:
On 14/02/2006 13:41, Emre Sevinc wrote:
function add(elementId) {
var container = document.getElementById(elementId);
for (var i = 0; i < 10; i++) {
var elt = document.createElement('div');
elt.innerHTML = "" + i;
[snip]
elt.onclick = function () {window.alert("" + i);};

[snip]
Could you please elaborate what the circular reference would be here?
Because I do not see it (yet).


elt -> elt.onclick
-> <Function object>
-> <Function object>.[[Scope]]
-> Variable object of add function
-> <Variable object>.elt

This is the same circular reference described in the FAQ notes.

Mike

--
Michael Winter
Prefix subject with [News] before replying by e-mail.
Feb 14 '06 #9

P: n/a
Michael Winter wrote:
On 14/02/2006 17:51, Thomas 'PointedEars' Lahn wrote:
On 14/02/2006 13:41, Emre Sevinc wrote:
function add(elementId) {
var container = document.getElementById(elementId);
for (var i = 0; i < 10; i++) {
var elt = document.createElement('div');
elt.innerHTML = "" + i; [snip] elt.onclick = function () {window.alert("" + i);};

[snip]
Could you please elaborate what the circular reference would be here?
Because I do not see it (yet).


elt -> elt.onclick
-> <Function object>
-> <Function object>.[[Scope]]
-> Variable object of add function
-> <Variable object>.elt

This is the same circular reference described in the FAQ notes.


Is it? So far I see no reference to the Variable Object of add's execution
context from the scope of the event listener. `i' is a local variable in
add's context storing a primitive (number) value and the closure defines
the event listener statically without any object reference to `add'.
PointedEars
Feb 14 '06 #10

P: n/a
Thomas 'PointedEars' Lahn wrote:
Michael Winter wrote:
On 14/02/2006 13:41, Emre Sevinc wrote:
function add(elementId) {
var container = document.getElementById(elementId);
for (var i = 0; i < 10; i++) {
var elt = document.createElement('div');
elt.innerHTML = "" + i;

[...]
// Beware!


Indeed...
elt.onclick = function () {window.alert("" + i);};


...this closure will form a circular reference involving DOM
nodes (container and elt). In IE, this will lead to a memory
leak[1]. The loop can be broken by assigning null before the
add function returns.


Could you please elaborate what the circular reference would
be here? Because I do not see it (yet).


The various DIVs created are all referred to by the element that they
are appended to, as its first and last child and through its childNodes
collection, and Each DIV refers to its neighbours as its previous and
next sibling. Each of those DIV elements has an onclick property that
refers to a function object. Each function object has an internal
[[Scope]] property that refers to its scope chain, each scope chain
includes (by reference) the Activation/Variable object of the single
execution context in which all the function objects were created, and
that Activation/Variable object has a - container - property referring
to the element to which the DIVs were appended and an - elt - property
referring to the last DIV created. That is quite a lot of circular
chains of reference including DOM nodes.

The apparently easiest points to break the chains are the two local
variables of the outer function, which could be assigned null before the
function returned. Unfortunately as the - container - element must have
an ID and IE makes IDed elements available as properties of the global
object, and the global object is at the end of all scope chains, nulling
the references won't actually solve the problem. The only real solution
to the memory leak here is to null the onclick properties of the DIV
elements (as the page unloads, if not sooner).

Richard.
Feb 14 '06 #11

This discussion thread is closed

Replies have been disabled for this discussion.