js中考察闭包的一个经典问题

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

第一问,运行以上代码会输出什么?
第二问,怎么修改代码才能输出0,1,2...9?

  1. 最后会输出10,10,10...10,这是因为传入的回调函数形成了一个闭包,会引用全局对象中的i,而闭包只能取得包含函数中任何变量的最后一个值,这里循环运行结束时i的值为10,因此最后输出的全都是10。
      而这里还有一个知识点就是,setTimeout是异步的,所以调用setTImeout时会将传入的回调函数放入事件队列中,等到主程序运行完成后才执行回调函数。我们可以把代码改成这样:
for(var i=0;i<10;i++){
  setTimeout(function(){console.log(i);},0);
}
console.log('done');

输出结果为先输出done再执行回调函数,也就是等到主程序运行完成后才会执行回调函数。

  1. 修改的方法有两种:
    (1)使用即时运行函数,将i通过参数传递,拷贝一份到外部函数作用域中:
for(var i=0;i<10;i++){
  setTimeout(function(i){
      return function(){console.log(i)}
    }(i),0);
}

(2)使用es6在块级作用域有效的let声明变量:

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

在这里,变量i只在块级作用有效,因此每次循环都是一个新的的变量i,而每个循环中的变量i的块级作用域因为存在于闭包的作用域链中,所以不会销毁,即保存了变量i的一个副本。
  如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容