3

I'm try to creat array with functions in a loop. But I think don't get something about encapsulation.

For example this code returns "y y". Live demo.

HTML

<div id="result"></div>

Javascript

var json = {
            '1':'x',
            '2':'y'
           };
var my_array = [];
var div = document.getElementById('result');

for (var key in json) {
    my_array.push(function() { 
        div.innerHTML = div.innerHTML + ' ' + json[key];
    });
};

var length = my_array.length;

for (var i = 0; i < length; i++) {
  my_function = my_array[i];
  my_function();
}

What should I do to get "x y"?

Tnx a lot.

5
  • Another case of bitten by closure Commented Jul 11, 2013 at 20:54
  • it does not work, because at the time u you calling the function the value of key variable is '2' Commented Jul 11, 2013 at 20:55
  • @KamenStoykov, oh i know why it doesn't work :) Commented Jul 11, 2013 at 20:56
  • Just a bit of curiosity here. Why do you need to have an array of functions? Isn't it even simpler to have an array of keys or whatever values that need to be used? Commented Jul 11, 2013 at 21:08
  • @Frederik.L I'm making waterfall with ajax requests Commented Jul 11, 2013 at 21:16

1 Answer 1

8

This is due to the way closures work in JavaScript.

You want something like this:

for (var key in json) {
    (function(key) {
        my_array.push(function() { 
            div.innerHTML = div.innerHTML + ' ' + json[key];
        });
    })(key);
}

In JavaScript, closures or anonymous functions are lexically bound to the scope in which they are defined. What this means is that they have access to all variables that have been defined in the scope in which the closure is also defined.

So in your original code, you have key, which initially points to 1. In your function, you have json[key], which originally is json[1], which is x. Then when the loop goes to the next iteration you have key set to 2. But the thing is that key in the first function instance and the second function instance both point to the same location. So when you finally evaluate the function, they will use whatever value key has at the time of execution. At the time of execution, key is set to 2 because that was the last value of key at the end of the loop.

To fix this, you have to introduce a new scope by using an anonymous, self-invoked function. By using this pattern, you deliberately introduce a new scope such that key in this new scope has its own location and is different from key in the outer scope.

Check out the updated fiddle.

Sign up to request clarification or add additional context in comments.

5 Comments

This doesn't work... Even if you can identify the problem, you might want to verify that your code works before submitting it as an answer
@taylorc93 Minor typo; fixed.
You don't "have to introduce a new scope by using an anonymous, self-invoked function". You can do it in a cleaner way by calling a function that passes the necessary things and returns a new function/scope.
@Ian That is another approach that would work as well. I guess it's a matter of personal preference, but I find it much more idiomatic to use an anonymous function in this manner rather than calling another function. You then have to jump to that function which can be a little jarring and cause some loss of context. Again, just a matter of personal preference.
@VivinPaliath thx a lot. Works fine. Special thanks for the comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.