JavaScript 闭包的写法应该用哪种

blacktulip · February 25, 2015 · Last by saxer replied at February 28, 2015 · 3410 hits

for (var i = 0; i < 10; i++) {
    setTimeout(function (a) {
        return function () {
            console.log(a);
        };
    }(i), 1000);
}

for (var i = 0; i < 10; i++) {
    (function(a) {
        setTimeout(function() {
            console.log(a);
        }, 1000);
    })(i);
}

这两种写法都能跑出正确结果,有没有一个 convention 说用哪一个比较好?还是其中某一种其实是有缺陷的?谢谢。

仔细想了一下,好像第二种写法更通用一些,一个 functionfor 循环里面的全打包了。不知道我的想法是否正确。

coffee 的 do 就是方便解决这个问题的

for i in 0...10
  do (i) ->
    setTimeout ->
      console.log i
    , 1000

有些 闭包 虽然看得懂,但找参数真不直观,我觉得第二种直观一点,一下就看到 function(a) 了

for (var i = 0; i < 10; i++) {
    setTimeout(function(a) {
        console.log(a);
    }, 1000, i);
}

#4 楼 @lifuzho Thanks

第二种其实也可以写成

for (var i = 0; i < 10; i++) {
    (function(a) {
        setTimeout(console.log(a), 1000);
    }(i));
}

两种写法没有什么差别,差别只在思路方面。

如果只是 setTimeout 的情况,如 @lifuzho 所写的,setTimeout 可以给回调函数传参数,而回调函数本身就可以当做一个闭包隔离外部环境。所以不需要单独写闭包。

如果是通用情况,我觉得第二种写法好一些。更通用和直观。不用关心 for 循环里面具体是什么逻辑。

不过在循环中反复声明匿名函数会有额外内存开销,我记得有些检查工具比如 jshint 会提示的。抽成一个函数反复调用比较好。

var asyncPrint = function(i) {
  setTimeout(function() {
    console.log(i);
  }, 100);
}

for (var i = 0; i < 10; i++) {
  asyncPrint(i);
}

1 秒后 0-9 都同时输出了吧? 楼主想要达到什么效果呢? 第一个 return function 好神奇,看不懂

@mogodb 你说得很对。

如果效果是要同时输出,楼主的两种写法都不好。呼叫一次 setTimeout 如果能达到效果,不用呼叫 10 次,因为 setTimeout 会与 call stack 交互,效果太强劲,能避免就避免。

另外,这个场景不需要闭包,function 参数就足够了。

按楼主要求同时输出:

setTimeout(function() {
   for (var i = 0; i < 10; i++) {
     console.log(i);
   }
}, 1000);

如果每次都延迟一秒,按@lifuzho的写法好,略加修改

for (var i = 0; i < 10; i++) {
    setTimeout(function(a) {
        console.log(a);
    }, 1000*i, i);
}

#5 楼 @blacktulip 这样写的话 setTimeout 没有起到任何实质上的作用,相当于

for (var i = 0; i < 10; i++) {
  console.log(i)
}

第二种,更直观

You need to Sign in before reply, if you don't have an account, please Sign up first.