闭包的意外行为:回调保存最后一个值

Unexpected behavior with closures: callback holds the last value

本文关键字:回调 保存 最后一个 意外 闭包      更新时间:2023-09-26

我会进入正题:我有这个循环:

for (var i = 1; i <= toSchedule; i++) {
        when = trackWrapper.lastPlay +
                (trackDuration +
                (looper.timeInterval - trackDuration));
        track.play(true, when);
        trackWrapper.lastPlay = when;
    }

play方法在体内有这个:

[...]
// Here when is a different value for each call (verified)
// Many calls of the play method are performed before the returned function below is run as a callback
        function playingCallback(myWhen){
            return function(buffers){
                // Here myWhen will always be equal to the value of the last call occurred BEFORE the first callback execution
                console.log("myWhen = "+myWhen);
                [...]
            };
        };
        var realCallback = playingCallback(when);
        track.scheduled.push(when);
        track.recorder.getBuffer(realCallback);

因此,例如:

play(true, 1);
play(true, 2);
play(true, 3);
// Wait for it...
myWhen = 3;
myWhen = 3;
myWhen = 3;
现在:我读过关于

闭包的文章,我读过关于"臭名昭著的循环问题"的文章,我在StackOverflow上读过几十个答案,但我无法弄清楚。这是我第二次遇到这种回调问题,所以,在这一点上,我想我还没有完全理解发生了什么。

你能向我解释上面的代码应该有什么问题吗?提前谢谢你。

通常,您应该了解以下规则:即使在退出范围之后,clousure 也可以访问其"周围范围"。但它将是执行时范围的状态,而不是闭包创建时(!

如果在循环中创建闭包,它将可以访问循环变量。但循环很可能已经结束。因此,循环变量将保存其最后一个循环的值。

因此,如果您的闭包是回调,则应在创建时创建相关范围变量的副本,并在执行时使用此副本。您可以做到这一点(例如),从立即执行的匿名函数创建内部闭包

function myOuterScope(count) {
   for(i=0; i<count; i++) {
      setTimeout((function(local_i) {
         // this function will be immediately executed. local_i is a copy of i at creation time
         return function() {
            // this is the function that will be called as a callback to setTimeout
            // use local_i here, and it will be 0, 1, 2 instead of 3, 3, 3
         }
      })(i)
      , 1000);
   }
}
myOuterScope(3);