【问题标题】:JavaScript Closure - Strange reference feature of anonymous function pushed to ArrayJavaScript Closure - 推送到数组的匿名函数的奇怪引用特性
【发布时间】:2013-11-03 07:58:42
【问题描述】:

在进行 nodejs 编程时,我发现了一个简单但有趣的问题。
为了轮流执行一组异步函数,我通常将“作业列表数组”与 async.series 一起使用。

按照以下步骤:

1.定义一个数组
2. 将作业函数推入数组。
3.使用async.series,顺序执行。
例如async.series(jobFunctionList, callback);

以下示例代码是简化的。
在评论中描述,它不像我预期的那样工作。

变量 'key' 和 'value' 变化
即使在 Job 函数被定义并被推入 jobList 数组之后。

似乎推送函数不断引用外部变量,
不是创建环境的值。


我找到了解决此问题的方法,但不知道为什么会这样。

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

*/

【问题讨论】:

    标签: javascript node.js closures push for-in-loop


    【解决方案1】:

    匿名函数使用(共享)外部范围的相同键和值变量。当for结束时,他们使用的值是key2 value2,而不是他们定义函数时的值。

    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
    

    解决方案是创建一个闭包,其中键和值都是本地的:

    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
        );
    }
    

    【讨论】:

      【解决方案2】:

      正如 Teemu 所说,这是因为当 for 循环结束时,值已经改变了

      你需要做的是:

      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])();
      

      虽然savedKeysavedValue 可以称为keyvalue,但它会指代新的,读起来可能更有意义

      【讨论】:

        猜你喜欢
        • 2013-04-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-01
        • 1970-01-01
        • 2022-11-02
        • 1970-01-01
        • 2014-05-01
        相关资源
        最近更新 更多