Please explains why this happens w/ anon functions

J

Jang

I noticed something interesting while I was doing some JavaScript.
Lets say I have the code below (simplified greatly from what I have
but it's just about the same).

var objs = {'foo' : {}, 'bar':{}, 'baz':{}};

for (var i in objs) {
objs.o = {};
objs.o.method = function() {
alert(i);
};
}

objs.foo.o.method(); // alerts baz
objs.bar.o.method(); // alerts baz
objs.baz.o.method(); // alerts baz

all three statements above will alert baz, even though the code's
logic should have it alert foo, bar, baz. Can someone explain why that
happens. Also, how would I fix that statement above to work as I
intended to? Thanks.
 
P

Peter Michaux

I noticed something interesting while I was doing some JavaScript.
Lets say I have the code below (simplified greatly from what I have
but it's just about the same).

var objs = {'foo' : {}, 'bar':{}, 'baz':{}};

for (var i in objs) {
objs.o = {};
objs.o.method = function() {
alert(i);
};

}

objs.foo.o.method(); // alerts baz
objs.bar.o.method(); // alerts baz
objs.baz.o.method(); // alerts baz

all three statements above will alert baz, even though the code's
logic should have it alert foo, bar, baz. Can someone explain why that
happens. Also, how would I fix that statement above to work as I
intended to? Thanks.


This definitely seems bizarre at first and this is closure related
issue. The short explaination is that when alert(i) is run it looks
inside the function you have written above and doesn't find an "i"
variable. So it continues up it's lookup chain to find the first "i".
When that for loop finished running the value of "i" was baz.

You are going to think it is pretty odd but you want to be writing
this...

var objs = {'foo' : {}, 'bar':{}, 'baz':{}};

for (var i in objs) {
objs.o = {};
objs.o.method = (function(i) {return function() {
alert(i);
};})(i);
}

objs.foo.o.method(); // alerts baz
objs.bar.o.method(); // alerts baz
objs.baz.o.method(); // alerts baz


After you figure out how this works and use this for a while you will
be glad it works this way :)

Unfortunately I don't know which video it was but if you watch the
Douglas Crockford videos on the Yahoo! UI Theater <URL:
http://developer.yahoo.com/yui/theater/> he discusses exactly this
issue. I think it is in one of the three videos called "Advanced
JavaScript". All of Douglas's videos are worth watching to get a sense
of how one experienced JavaScript user does things.

Since you are using alerts I'm just going to mention it just in
case...you know about the Firefox extension Firebug, yes? <URL:
http://getfirebug.com> and then you can use console.log().

Peter
 
J

Jang

I noticed something interesting while I was doing some JavaScript.
Lets say I have the code below (simplified greatly from what I have
but it's just about the same).
var objs = {'foo' : {}, 'bar':{}, 'baz':{}};
for (var i in objs) {
objs.o = {};
objs.o.method = function() {
alert(i);
};

objs.foo.o.method(); // alerts baz
objs.bar.o.method(); // alerts baz
objs.baz.o.method(); // alerts baz

all three statements above will alert baz, even though the code's
logic should have it alert foo, bar, baz. Can someone explain why that
happens. Also, how would I fix that statement above to work as I
intended to? Thanks.

This definitely seems bizarre at first and this is closure related
issue. The short explaination is that when alert(i) is run it looks
inside the function you have written above and doesn't find an "i"
variable. So it continues up it's lookup chain to find the first "i".
When that for loop finished running the value of "i" was baz.

You are going to think it is pretty odd but you want to be writing
this...

var objs = {'foo' : {}, 'bar':{}, 'baz':{}};

for (var i in objs) {
objs.o = {};
objs.o.method = (function(i) {return function() {
alert(i);
};})(i);

}

objs.foo.o.method(); // alerts baz
objs.bar.o.method(); // alerts baz
objs.baz.o.method(); // alerts baz

After you figure out how this works and use this for a while you will
be glad it works this way :)

Unfortunately I don't know which video it was but if you watch the
Douglas Crockford videos on the Yahoo! UI Theater <URL:http://developer.yahoo.com/yui/theater/> he discusses exactly this
issue. I think it is in one of the three videos called "Advanced
JavaScript". All of Douglas's videos are worth watching to get a sense
of how one experienced JavaScript user does things.

Since you are using alerts I'm just going to mention it just in
case...you know about the Firefox extension Firebug, yes? <URL:http://getfirebug.com> and then you can use console.log().


Actually right after I posted, I figured out I can fix it using the
exact method you wrote. I was hoping if there was a "cleaner"
solution. :)

But as I read your post and thought about I think I'm beginning to
understand why that happens. The anonymous function seems to be
evaluated at run time rather than at compile time. And during runtime,
the latest value of i is going to be 'baz.'
 
P

Peter Michaux

Peter said:
I noticed something interesting while I was doing some JavaScript.
Lets say I have the code below (simplified greatly from what I have
but it's just about the same).
var objs = {'foo' : {}, 'bar':{}, 'baz':{}};
for (var i in objs) {
objs.o = {};
objs.o.method = function() {
alert(i);
};
}
objs.foo.o.method(); // alerts baz
objs.bar.o.method(); // alerts baz
objs.baz.o.method(); // alerts baz
all three statements above will alert baz, even though the code's
logic should have it alert foo, bar, baz. Can someone explain why that
happens. Also, how would I fix that statement above to work as I
intended to? Thanks.

This definitely seems bizarre at first and this is closure related
issue. The short explaination is that when alert(i) is run it looks
inside the function you have written above and doesn't find an "i"
variable. So it continues up it's lookup chain to find the first "i".
When that for loop finished running the value of "i" was baz.
You are going to think it is pretty odd but you want to be writing
this...
var objs = {'foo' : {}, 'bar':{}, 'baz':{}};
for (var i in objs) {
objs.o = {};
objs.o.method = (function(i) {return function() {
alert(i);
};})(i);
}


This is basically creating closures, right?
Yes
objs.foo.o.method(); // alerts baz
objs.bar.o.method(); // alerts baz
objs.baz.o.method(); // alerts baz

I think you meant:

objs.foo.o.method(); // alerts foo
objs.bar.o.method(); // alerts bar
objs.baz.o.method(); // alerts baz

;)


I'm supposed to update comments too? shesh.

Peter
 
L

-Lost

Peter said:
Peter said:
I noticed something interesting while I was doing some JavaScript.
Lets say I have the code below (simplified greatly from what I have
but it's just about the same).
var objs = {'foo' : {}, 'bar':{}, 'baz':{}};
for (var i in objs) {
objs.o = {};
objs.o.method = function() {
alert(i);
};
}
objs.foo.o.method(); // alerts baz
objs.bar.o.method(); // alerts baz
objs.baz.o.method(); // alerts baz
all three statements above will alert baz, even though the code's
logic should have it alert foo, bar, baz. Can someone explain why that
happens. Also, how would I fix that statement above to work as I
intended to? Thanks.
This definitely seems bizarre at first and this is closure related
issue. The short explaination is that when alert(i) is run it looks
inside the function you have written above and doesn't find an "i"
variable. So it continues up it's lookup chain to find the first "i".
When that for loop finished running the value of "i" was baz.
You are going to think it is pretty odd but you want to be writing
this...
var objs = {'foo' : {}, 'bar':{}, 'baz':{}};
for (var i in objs) {
objs.o = {};
objs.o.method = (function(i) {return function() {
alert(i);
};})(i);
}

This is basically creating closures, right?
Yes
objs.foo.o.method(); // alerts baz
objs.bar.o.method(); // alerts baz
objs.baz.o.method(); // alerts baz
I think you meant:

objs.foo.o.method(); // alerts foo
objs.bar.o.method(); // alerts bar
objs.baz.o.method(); // alerts baz

;)


I'm supposed to update comments too? shesh.


Haha. Well, I figured you pasted it anew, you would probably update it
to reflect your changes. ;)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,239
Members
46,827
Latest member
DMUK_Beginner

Latest Threads

Top