Skip to content

Instantly share code, notes, and snippets.

@tobi
Created April 9, 2011 21:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tobi/911797 to your computer and use it in GitHub Desktop.
Save tobi/911797 to your computer and use it in GitHub Desktop.
wtf.js
var ary = ['a', 'b', 'c'];
var funcs = [];
for (var i = 0; i < ary.length; i++) {
var c = ary[i]
funcs.push(function() { console.log(c) })
};
for (var i = 0; i < funcs.length; i++) {
funcs[i]();
};
@tobi
Copy link
Author

tobi commented Apr 9, 2011

gives
c
c
c

why?

@sri
Copy link

sri commented Apr 9, 2011

because all the functions see the same 'c'.

try this:

funcs.push((function(c) { return function() { console.log(c) } })(c))

@Tyrael
Copy link

Tyrael commented Apr 9, 2011

my guess is that c will be only accessed when the function is called, hence it will contain the same value: the value of the last iteration.
but I didn't play with js recently, so I can be wrong.

@biilmann
Copy link

biilmann commented Apr 9, 2011

The binding scope doesn't change within a for loop, so you're assigning 3 different values to the same variable. In the end the result is just c = 'c'.

This is nothing javascript specific, heres the same code in ruby (very un-rubyish though):

ary = ['a', 'b', 'c']
funcs = []

for i in 1..ary.length
  c = ary[i-1]

  funcs.push lambda { puts c }
end

funcs.each do |f|
  f.call
end

Obviously you never tend to run into this problem in Ruby because of it's blocks and iterators...

@tobi
Copy link
Author

tobi commented Apr 9, 2011

fascinating!

@jboesch
Copy link

jboesch commented Apr 9, 2011

They explain the issues under the "Closures" section: http://bonsaiden.github.com/JavaScript-Garden/#function.closures

And this is prob what you want :)
http://jsfiddle.net/jboesch26/VWKX7/1/

Further reading about it... http://www.mennovanslooten.nl/blog/post/62

@tlrobinson
Copy link

This is one reason I like forEach and other functional friends...

ary.forEach(function(c) {
    funcs.push(function() { console.log(c); });
});

...will work how you want. Or even better:

var funcs = ary.map(function(c) {
    return function() { console.log(c); };
});

Older browsers don't have these functions, but they can easily be added: https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js#L182-210

If performance is critical you may want to use the traditional for loop, since using forEach incurs a function call per item.

I wish something like this would work but it doesn't quite...

var funcs = ary.map(console.log.bind);

@narensisodiya
Copy link

I think, the same c is visible to all function, which have the latest assigned value.
as variable scope in javaScripts is only function level so you can initialize var c in function that will work,

  funcs.push(function() { var c = ary[i]; console.log(c) }) 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment