Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Javascript > Closure bug

Reply
Thread Tools

Closure bug

 
 
Eleandor
Guest
Posts: n/a
 
      05-16-2010
Well, I realise it's not so much a bug as it is a feature.

I've written a quick testpage:

<html>
<head>
<script type="text/javascript">
Person = (function() {
return function() {
var self = this;

this.messages = function() {
for(var i = 0; i < 10; i++) {
var elm = document.createElement("div");
elm.innerHTML = "Display " + i;
elm.onclick = function() {
self.shout(i);
};
document.body.appendChild(elm);
}
};

this.shout = function(number) {
alert(number);
};
};
}());

function LoadPage() {
var p = new Person();
p.messages();
}

</script>
</head>

<body onload="LoadPage()">
</body>
</html>

The html page shows "Display x" with x a number from 0 to 9. However,
as I click it, it displays 10 regardless of which element I clicked. I
can understand why, since the value of i is 10 when the loop ends. But
what I'm trying to achieve is to create a function that uses the
actual value of i, at the moment when the onclick function is created.
So "Display 5" should actually display 5, using the literal value of i
when the function is created, instead of the value of i at the time of
execution.

How can I achieve something like this?

Thanks,

- Bart
 
Reply With Quote
 
 
 
 
RobG
Guest
Posts: n/a
 
      05-17-2010
On May 17, 8:44*am, Eleandor <(E-Mail Removed)> wrote:
> Well, I realise it's not so much a bug as it is a feature.
>
> I've written a quick testpage:
>
> <html>
> * * * * <head>


When posting code, indent using 2 (preferred) or 4 spaces and manually
wrap at about 70 characters, see below.

<head>
<script type="text/javascript">

Person = (function() {
return function() {
var self = this;
this.messages = function() {

for(var i = 0; i < 10; i++) {
var elm = document.createElement("div");
elm.innerHTML = "Display " + i;
elm.onclick = function() {
self.shout(i);
};
document.body.appendChild(elm);
}
};
this.shout = function(number) {
alert(number);
};
};
}());

function LoadPage() {
var p = new Person();
p.messages();
}
</script>
</head>
<body onload="LoadPage()"></body>

>
> The html page shows "Display x" with x a number from 0 to 9. However,
> as I click it, it displays 10 regardless of which element I clicked. I
> can understand why, since the value of i is 10 when the loop ends. But
> what I'm trying to achieve is to create a function that uses the
> actual value of i, at the moment when the onclick function is created.
> So "Display 5" should actually display 5, using the literal value of i
> when the function is created, instead of the value of i at the time of
> execution.
>
> How can I achieve something like this?


You need to break the closure. One way is to use new Function, but
scope becomes a bit tricky. Another is to use a setter for the onclick
property rather than a function expression, e.g.

elm.onclick = setOnclick(self, i);


then add:

function setOnclick(self, i) {
return function() {
self.shout(i);
}
}

as a global function, or inside the outer Person function (the
Cornified one[1]).

Another is to set it with a function expression:

elm.onclick = (function(number) {
return function() {
self.shout(number);
};
})(i);


But this whole thing seems quite convoluted. The i property doesn't
seem to belong to a "person", it belongs to the listener attached to
the element, so probably shouldn't be inside the person constructor at
all. Perhaps you are better to create a message cache and use an
attribute of the HTML element to associate with a message (e.g. its
ID). If HTML 5 was widely implemented, you could add the index as a
data property of the element directly (but it will be a long time
before that is viable on the web).

There are many other solutions, hard to say what is better or worse
without knowing what you are really trying to do, I doubt that either
of the above suggestions are the best you can do in your circumstance.

1. <URL: http://groups.google.com/group/comp....ec4b30a4e2811#
>



--
Rob
 
Reply With Quote
 
 
 
 
Thomas 'PointedEars' Lahn
Guest
Posts: n/a
 
      05-17-2010
RobG wrote:

> Eleandor wrote:
>> I've written a quick testpage:
>>
>> <html>
>> <head>

>
> When posting code, indent using 2 (preferred) or 4 spaces and manually
> wrap at about 70 characters, see below.


ACK

> <head>


That HTML document needs a DOCTYPE declaration and a TITLE element to be
Valid. <http://validator.w3.org/>

> <script type="text/javascript">
>
> Person = (function() {


Should be declared a (global) variable, otherwise there can be fatal
side-effects with MSHTML (fatal as in "breaking").

var Person = ...

> return function() {
> var self = this;
> this.messages = function() {
>
> for(var i = 0; i < 10; i++) {
> var elm = document.createElement("div");
> elm.innerHTML = "Display " + i;
> elm.onclick = function() {
> self.shout(i);
> };
> document.body.appendChild(elm);


You should avoid combining DOM Level 2+ and 0, especially when without
feature test. In particular, you do not need or want `innerHTML' here:

var elm = document.createElement("div");
if (elm)
{
elm.appendChild(document.createTextNode("Display " + i));
// ...
}

> }
> };
> this.shout = function(number) {
> alert(number);
> };
> };
> }());
>
> function LoadPage() {


This is not used as a constructor or factory, so the identifier should start
lowercase.

> [...] or inside the outer Person function (the Cornified one[1]). [...]

^^^^^^^^^
Please don't. I thought it was only a joke back then :-/


PointedEars
--
Use any version of Microsoft Frontpage to create your site.
(This won't prevent people from viewing your source, but no one
will want to steal it.)
-- from <http://www.vortex-webdesign.com/help/hidesource.htm> (404-comp.)
 
Reply With Quote
 
Matt Kruse
Guest
Posts: n/a
 
      05-17-2010
On May 16, 5:44*pm, Eleandor <(E-Mail Removed)> wrote:
> what I'm trying to achieve is to create a function that uses the
> actual value of i, at the moment when the onclick function is created.
> So "Display 5" should actually display 5, using the literal value of i
> when the function is created, instead of the value of i at the time of
> execution.


elm.onclick = (function(inner_i) {
return function() {
self.shout(inner_i);
}
})(i);

Be careful for memory leaks...

Matt Kruse
 
Reply With Quote
 
RobG
Guest
Posts: n/a
 
      05-18-2010
On May 18, 3:59 am, williamc <(E-Mail Removed)> wrote:
[...]
> Working through the example below helped me when I was reading the Zakas
> book not too long ago. From a notes page...
>
> * * *
>
> 4. Inner functions that retain values from outer functions possess the
> last value from the outer function.


Not so. The inner function has the outer function's variable object on
its scope chain, the identifier - i - will resolve to it if no local
variable shadows it. If the value of the variable changes, then its
value changes in the inner function too, e.g.


var outer = (function () {
var x;

return {

getX: function () {
return x;
},

setX: function(val) {
x = val;
}
}
})();

// Check the value of x
alert(outer.getX()); // undefined

// Set the value of x to a string
outer.setX('foo');

// Check the value of x
alert(outer.getX()); // foo

So the value isn't "set" when the function exits. The value is
whatever it is when it is read and can be changed by some other
closure (emulating a privileged method).


> This can lead to unexpected results
> as demonstrated in the first function below, which the programmer
> expected to return an array of functions which each will return the
> value of their array index. Instead, each function returns 5.


Unexpected only if the programmer isn't aware of how closures work in
ECMAScript.


> The second function creates the desired array of functions. Zakas: "The
> anonymous function has one argument, num, which is the number that the
> result function should return. Since function arguments are passed by
> value, the current value of i is copied into the argument num."


More misdirection. They are always references, but the *value* might
be a primitive or an object. Primitives are immutable, you can't
modify them, you can only assign a new one, so:

// With primitives
var a = 5,
b = a; // a and b == 5
b = 6; // b is assigned a new value, a is still == 5

// With objects
var o = {},
p = o; // o and p == object assigned to o
p = {}; // p is assigned a new object, o hasn't changed

The difference is that you can modify an object's properties, so:

var o = {},
p = o; // o and p == object assigned to o
p.x = 'foo';
alert(o.x); // foo

The the concept that "function arguments are passed by value" might be
kind of true if function arguments are always primitives, but they
aren't.


> function createFunctions() {
> var result = new Array();
> for (var i = 0; i < 5; i++) {
> result[i] = function() {
> return i;
> };
> }
> return result;
> }
> arrTest = createFunctions();
> alert(arrTest[2]()); // 5, not 2!
>
> function createFunctions2() {
> var result = new Array();
> for (var i = 0; i < 5; i++) {
> result[i] = function(num) {
> return function() {
> return num;
> };
> }(i);
> }
> return result;
> }
> arrTest = createFunctions2();
> alert(arrTest[2]()); // now it's 2


Because of the new closure with num. Variable i is still there on the
inner function's scope chain, replace:

return num;

with

return i;

and you get 5 again.


--
Rob
 
Reply With Quote
 
RobG
Guest
Posts: n/a
 
      05-18-2010
On May 18, 3:56*am, Matt Kruse <(E-Mail Removed)> wrote:
> On May 16, 5:44*pm, Eleandor <(E-Mail Removed)> wrote:
>
> > what I'm trying to achieve is to create a function that uses the
> > actual value of i, at the moment when the onclick function is created.
> > So "Display 5" should actually display 5, using the literal value of i
> > when the function is created, instead of the value of i at the time of
> > execution.

>
> elm.onclick = (function(inner_i) {
> * return function() {
> * * self.shout(inner_i);
> * }
>
> })(i);
>
> Be careful for memory leaks...


Yes, forgot to mention that. They can be avoided for the most part by
setting:

elm = null;

in the function where elm is declared just before it ends.


--
Rob
 
Reply With Quote
 
Thomas 'PointedEars' Lahn
Guest
Posts: n/a
 
      05-18-2010
RobG wrote:

> On May 18, 3:59 am, williamc <(E-Mail Removed)> wrote:
> [...]
>> The second function creates the desired array of functions. Zakas: "The
>> anonymous function has one argument, num, which is the number that the
>> result function should return. Since function arguments are passed by
>> value, the current value of i is copied into the argument num."

>
> More misdirection. They are always references,


What do you mean by "they" here?

> but the *value* might be a primitive or an object.


No, the value can be a primitive value or an object reference.
You cannot access an object directly, you need a reference to it.
As a result, there can be multiple references to the same object.
The object may be subject to garbage collection if it is no longer
referred, i.e. there are no more references to it (much like with
hardlinks).

> The the concept that "function arguments are passed by value" might be
> kind of true if function arguments are always primitives, but they
> aren't.


Zakas' premise is right here; you are not. Object references are values.
See also
<https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Functions>,
which was agreed on here.


PointedEars
--
Use any version of Microsoft Frontpage to create your site.
(This won't prevent people from viewing your source, but no one
will want to steal it.)
-- from <http://www.vortex-webdesign.com/help/hidesource.htm> (404-comp.)
 
Reply With Quote
 
Thomas 'PointedEars' Lahn
Guest
Posts: n/a
 
      05-18-2010
Stefan Weiss wrote:

> Eleandor wrote:
>> Person = (function() {
>> return function() {
>> var self = this;

> ...
>
> Minor nitpick: self is not a good variable name in browser scripting;
> it's usually an alias for the window object. It won't cause a problem in
> your example, but it could confuse people who see a call to
> "self.shout()" before they see the assignment above.


It is not "usually an alias for the window object", it is a property of
Window instances or their prototype to refer to the instance. A Window
instance's prototype may be in the prototype chain of the ECMAScript Global
Object (this is the case with client-side JavaScript 1.8.2 in Gecko
1.9.2.3).

Since to my knowledge it is unnecessary to access the `self' property of
Window instances, I can see no cause for confusion here. Indeed, the
declaration follows the pattern of several other OOPLs, including most
notably, IIUC, Smalltalk which influenced the prototype-based Self
programming language (where you can omit `self') which influenced JavaScript
(unfortunately, Brendan Eich apparently did not see the importance of the
`self' keyword in JavaScript's predecessors).

However, in this case that assignment probably should be moved before the
`for' loop. There is no need (and perhaps no want) of the `self' identifier
to be in the scope of the returned function. But the function expression
returning a function when called is quite pointless and therefore
inefficient here to begin with; the assignment of a function expression, or
a function declaration would suffice as there are no bound variables in the
outer scope.


PointedEars
--
Prototype.js was written by people who don't know javascript for people
who don't know javascript. People who don't know javascript are not
the best source of advice on designing systems that use javascript.
-- Richard Cornford, cljs, <f806at$ail$1$(E-Mail Removed)>
 
Reply With Quote
 
David Mark
Guest
Posts: n/a
 
      05-18-2010
williamc wrote:
> On 5/18/2010 6:52 AM, Thomas 'PointedEars' Lahn wrote:
>> RobG wrote:
>>
>>> On May 18, 3:59 am, williamc <(E-Mail Removed)> wrote:
>>> [...]
>>>> The second function creates the desired array of functions. Zakas: "The
>>>> anonymous function has one argument, num, which is the number that the
>>>> result function should return. Since function arguments are passed by
>>>> value, the current value of i is copied into the argument num."
>>> More misdirection. They are always references,

>> What do you mean by "they" here?
>>
>>> but the *value* might be a primitive or an object.

>> No, the value can be a primitive value or an object reference.
>> You cannot access an object directly, you need a reference to it.
>> As a result, there can be multiple references to the same object.
>> The object may be subject to garbage collection if it is no longer
>> referred, i.e. there are no more references to it (much like with
>> hardlinks).
>>
>>> The the concept that "function arguments are passed by value" might be
>>> kind of true if function arguments are always primitives, but they
>>> aren't.

>> Zakas' premise is right here; you are not. Object references are values.
>> See also
>> <https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Functions>,
>> which was agreed on here.
>>

>
> I wondered what people in c.l.j would say about the Zakas' point that
> function arguments are always passed by value. He kind of stresses it,
> in the sense that he repeats it several times. I didn't really feel
> comfortable with it at the time, and I still don't feel comfortable with
> it. In most languages doesn't the concept of pass by value vs. pass by
> reference refer to passing an independent copy vs. passing a reference
> to the thing itself?
>
> I have a "something I'm missing here" feeling...
>
> obj = {
> propX: "foo"
> };
> num = 27;
>
> function test(argNum, argObj) {
> argNum = 28;
> argObj.propX = "bar";
> }
>
> test(num, obj);
>
> console.log(num); // still 27
> console.log(obj.propX); // "bar", so why isn't this called a pass
> by reference?
>
> In the link Thomas cited it says...
>
> "The parameters of a function call are the function's arguments.
> Arguments are passed to functions by value. If the function changes the
> value of an argument, this change is not reflected globally or in the
> calling function. However, object references are values, too, and they
> are special: if the function changes the referred object's properties,
> that change is visible outside the function, ... "
>
> So, I guess I'm having a hard time with the "they are special". Wouldn't
> this specialness simply be called a pass by reference in most languages?
>


No. The value passed is a reference to an object. That might sound
like a contradiction in terms, but consider:-

function test(o) {
o = null;
}

var p = {};
test(p);
window.alert(p); // Not null
 
Reply With Quote
 
Thomas 'PointedEars' Lahn
Guest
Posts: n/a
 
      05-18-2010
williamc wrote:

> David Mark wrote:
>> No. The value passed is a reference to an object. That might sound
>> like a contradiction in terms, but consider:-
>>
>> function test(o) {
>> o = null;
>> }
>>
>> var p = {};
>> test(p);
>> window.alert(p); // Not null

>
> Yikes! Well, that's certainly a good example, as I would have expected p
> to be null after the function call. But I can't say that I've succeeded
> in understanding why it isn't.


As `o' is but a variable (sort of) that is assigned a value, a reference to
an object, as execution enters the context of the function [ES3/5, 10.4] --

p
||
ref ---> [object Object]
||
o

-- then changing the value of the (local) "variable" in the function does
not change the object, of course:

p
||
ref ---> [object Object]

o = null

That is where object references are special. As long as a property (here:
of the function context's Variable Object) stores a reference value, you can
use that property to change the referred object's properties.

> So, the right way to think about is the object "supplies the value to
> the function"?


No, `p' did not store the object in the first place; it only stored the
reference (value) to it. As does `q' below:

var p = {};

/*
* p
* ||
* ref ---> [object Object = {}]
*/

var q = p;

/*
* p
* ||
* ref ---> [object Object = {}]
* ||
* q
*/

q.a = 42;

/*
* p
* ||
* ref ---> [object Object = {a: 42}]
* ||
* q
*/

/* 42 */
p.a;

BTW, it's the same in Java and other OOPLs.

You want to fix your `From' header.


HTH

PointedEars
--
realism: HTML 4.01 Strict
evangelism: XHTML 1.0 Strict
madness: XHTML 1.1 as application/xhtml+xml
-- Bjoern Hoehrmann
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
*bug* *bug* *bug* David Raleigh Arnold Firefox 12 04-02-2007 03:13 AM
sys.settrace closure interaction bug Scott_Marks Python 0 10-01-2006 04:25 PM
Page Closure JezB ASP .Net 1 12-03-2003 02:05 PM
Automatic closure of a CGI-generated page. Ivan Sutton Java 0 10-01-2003 05:30 PM
Perl hangs when returning lvalue closure from another lvalue closure Julian Mehnle Perl Misc 0 07-17-2003 03:13 PM



Advertisments