473,746 Members | 2,211 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

JavaScript, Higher Order Functions, Closures, how come?


Take a look at the following snippet:

<html>
<head>
<script>
function add(elementId) {
var container = document.getEle mentById(elemen tId);
for (var i = 0; i < 10; i++) {
var elt = document.create Element('div');
elt.innerHTML = "" + i;

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

</head>

<body>
<div id=myDiv onClick=add('my Div')>
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
10 2075
Emre Sevinc wrote:
<html>
<head>
<script>
function add(elementId) {
var container = document.getEle mentById(elemen tId);
for (var i = 0; i < 10; i++) {
var elt = document.create Element('div');
elt.innerHTML = "" + i;

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

</head>

<body>
<div id=myDiv onClick=add('my Div')>
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(v alue);};
};

...
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
>>>>> "DB" == Duncan Booth <du**********@i nvalid.invalid> writes:

DB> Emre Sevinc wrote:
<html> <head> <script> function add(elementId) { var container
= document.getEle mentById(elemen tId); 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(v alue);}; };

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


Emre Sevinc wrote:

function add(elementId) {
var container = document.getEle mentById(elemen tId);
for (var i = 0; i < 10; i++) {
var elt = document.create Element('div');
elt.innerHTML = "" + i;

// Beware!
elt.onclick = function () {window.alert(" " + i);};
container.appen dChild(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.create Element('div');
elt.appendChild (document.creat eTextNode(i));
elt.onclick = function () {window.alert(" " + i);};
container.appen dChild(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.create Element('div');
elt.appendChild (document.creat eTextNode(i));
elt.onclick = function () { alert(++i); };
container.appen dChild(elt);
if (i == 5) {
elt.onclick();
}
}
}
add();


Check the FAQ notes on closures:
<http://www.jibbering.c om/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
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.getEle mentById(elemen tId);
for (var i = 0; i < 10; i++) {
var elt = document.create Element('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('my Div')>
That onclick attribute value must be quoted:

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

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.getEle mentById(id);

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

element.appendC hild(document.c reateTextNode(i ));
element.onclick = new Function('alert (\'' + i + '\');');

container.appen dChild(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.c om/faq/faq_notes/closures.html#c lMem>

--
Michael Winter
Prefix subject with [News] before replying by e-mail.
Feb 14 '06 #5
Emre Sevinc wrote:
>> "DB" == Duncan Booth <du**********@i nvalid.invalid> writes:
DB> Emre Sevinc wrote: >> <html> <head> <script> function add(elementId) { var container
>> = document.getEle mentById(elemen tId); 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.getEle mentById(elemen tId);
for (var i = 0; i < 10; i++) {
var elt = document.create Element('div');
elt.innerHTML = "" + i;

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

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

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

var container = document.getEle mentById(elemen tId);
for (var i = 0; i < 10; i++) {
var elt = document.create Element('div');
elt.innerHTML = "" + i;

// Beware!
elt.onclick = callme;
container.appen dChild(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

Hello Emre,
Emre Sevinc <em***@bilgi.ed u.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
Michael Winter wrote:
On 14/02/2006 13:41, Emre Sevinc wrote:
function add(elementId) {
var container = document.getEle mentById(elemen tId);
for (var i = 0; i < 10; i++) {
var elt = document.create Element('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
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.getEle mentById(elemen tId);
for (var i = 0; i < 10; i++) {
var elt = document.create Element('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
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.getEle mentById(elemen tId);
for (var i = 0; i < 10; i++) {
var elt = document.create Element('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

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

699
34057
by: mike420 | last post by:
I think everyone who used Python will agree that its syntax is the best thing going for it. It is very readable and easy for everyone to learn. But, Python does not a have very good macro capabilities, unfortunately. I'd like to know if it may be possible to add a powerful macro system to Python, while keeping its amazing syntax, and if it could be possible to add Pythonistic syntax to Lisp or Scheme, while keeping all of the...
3
3617
by: Anonymous | last post by:
Hello folks -- I would like a JavaScript tutorial for programmers. By this I mean that I'd like to know the language "fundamentals" /first/ and then learn about the "web stuff". This is because I came accross some articles claiming that JavaScript is, in fact, quite full of interesting capabilities (closures, higher-order functions, anonymous funtions, even object can be supported as in OOP). Can anyone recommend me a tutorial that goes...
4
4811
by: Derek | last post by:
Hi, I've built a rather large CGI that dumps a lot of data and a fairly complex javascript app out to the client's browser. Granted this may be poor style according to someone web design philosophy but that is the way things need to work for now here. The problem I'm having is that it appears that the browsers (IE, mozilla and netscape) are sometimes getting confused about wether the javascript code is running. By this I mean when I...
53
5737
by: Cardman | last post by:
Greetings, I am trying to solve a problem that has been inflicting my self created Order Forms for a long time, where the problem is that as I cannot reproduce this error myself, then it is difficult to know what is going on. One of these Order Forms you can see here... http://www.cardman.co.uk/orderform.php3
136
9432
by: Matt Kruse | last post by:
http://www.JavascriptToolbox.com/bestpractices/ I started writing this up as a guide for some people who were looking for general tips on how to do things the 'right way' with Javascript. Their code was littered with document.all and eval, for example, and I wanted to create a practical list of best practices that they could easily put to use. The above URL is version 1.0 (draft) that resulted. IMO, it is not a replacement for the FAQ,...
2
3059
by: Jake Barnes | last post by:
Using javascript closures to create singletons to ensure the survival of a reference to an HTML block when removeChild() may remove the last reference to the block and thus destory the block is what I'm hoping to achieve. I've never before had to use Javascript closures, but now I do, so I'm making an effort to understand them. I've been giving this essay a re-read: http://jibbering.com/faq/faq_notes/closures.html
10
12804
by: John Passaniti | last post by:
(Note: This is not the same message I posted a week or so ago. The problem that prevented my previous attempt to work was a silly error in the template system I was using. This is a problem involving variable scope in JavaScript.) I have a lot of code that generates HTML on the fly. This code has tags with id attributes derived from variables. A small example: blah('<span id="' + dev + '_' + mod + '">...</span>');
6
4207
by: Xcriber51 | last post by:
Hi I've come here after googling for this a day or two and not being able to spot what I'm looking for. I need a JavaScript IDE -- no frills, nothing special -- that offers me the ability to - directly call my functions - step through them
4
1846
by: MartinRinehart | last post by:
I've written a short article explaining closures in JavaScript. It's at: http://www.martinrinehart.com/articles/javascript-closures.html I think I've understood. I look forward to your constructive critique.
0
8796
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
9491
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
1
9280
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
9214
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
8221
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
6057
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4573
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
4829
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
3
2196
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.