简体   繁体   中英

JavaScript Closure - Strange reference feature of anonymous function pushed to Array

While working on nodejs programming, I found a simple, but interesting problem.
To execute a bundle of asynchronous functions in turns, I usually use 'job list array' with async.series.

By following steps :

1. Define an array
2. Push job functions into array.
3. Using async.series, execute it sequentially.
eg async.series(jobFunctionList, callback);

Following example code is simplified one.
Described in comment, it doesn't work as I expected.



I found a solution to resolve this problem, but don't know why it works as that.

var dataList = { key1: 'value1', key2: 'value2' };

var jobList = new Array();

for (var key in dataList)
{
    var value = dataList[key];

    jobList.push(
        function (next)
        {
            console.log(key + ' : ' + value);
        }
    );
}

(jobList[0])();
(jobList[1])();

/* Expected Output :

key1 : value1
key2 : value2

*/

/* Real Output :

key2 : value2  <--- WHY ???
key2 : value2

*/

As Teemu stated, its because by the time that the for loop finished, the value has already changed

What you need to do is the following:

var dataList = { key1: 'value1', key2: 'value2' };

var jobList = new Array();

for (var key in dataList)
{
    var value = dataList[key];

    jobList.push(
        (function(savedKey, savedValue) {
            return function (next) {
                console.log(savedKey + ' : ' + savedValue);
            }
        })(key, value)
    );
}

(jobList[0])();
(jobList[1])();

Although savedKey and savedValue can be called key and value and it will refer to the new one, which may make more sense reading it

The anonymous functions use(share) the same key and value variables from the outer scope. When the for is over, the values they use are key2 value2, not the values they had when the functions were defined.

for (var key in dataList) // definition of key
{
    var value = dataList[key]; // definition of value

    jobList.push(
        function (next)
        {
            console.log(key + ' : ' + value); // uses key and value from the outer scope
        }
    );
}

(jobList[0])(); // key and value are key2 value2 after for, so they are printed
(jobList[1])(); // same

The solution is to create a closure in which key and value are local:

for (var key in dataList) // definition of key
{
    var value = dataList[key]; // definition of value

    jobList.push(
      function(k,v){ // k,v local copies of key and value
        return function (next)
          {
            console.log(k + ' : ' + v); // uses local copies, created one for each iteration
          }
      }(key,value); // immediately execute the outer anonymous function, it just creates a local scope
    );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM