JavaScript 闭包的写法应该用哪种

blacktulip · 2015年02月25日 · 最后由 saxer 回复于 2015年02月28日 · 2912 次阅读

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 说用哪一个比较好?还是其中某一种其实是有缺陷的?谢谢。

共收到 10 条回复

仔细想了一下,好像第二种写法更通用一些,一个 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)
}

第二种,更直观

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册