JS闭包学习笔记(2):循环和闭包
2016-07-07 11:11
197 查看
经典例子:
最终的结果并不如我们期待的打印出1,2,3,4,5,而是打印出6,6,6,6,6.
setTimeout的回调函数timer会在循环结束后才开始执行,因此会输出i最后的值6.即使将setTimeout的第二个参数设为0,timer还是会在循环结束后才执行,因此还是会打印6.
我们本来期待每一次循环中都能保留当时的i,但当闭包起作用时,虽然每个函数是在各次循环中依次定义的,但它们最后都share同一个作用域,即全局作用域,因此都使用同一个i。五个函数其实只是一个接一个地被声明,其中并没有循环存在了。
如何改进呢?
首先想到使用IIFE(立即执行函数):
IIFE的确增加了一层作用域,每次setTimeout执行都会闭包其当前循环中的IIFE作用域,此时的确每个timer函数闭包了自己的作用域,但IIFE是空的,最后导致每一个timer还是会调用同一个全局变量i,值为6.
从空的IIFE进行改进:每个函数都需要它自己的变量,因此要在在每次循环中保存当时的i:
这回才得出正确的结果!
另一种写法:
每一次循环中的IIFE为当次循环创建了一个新的作用域,这样其内部的setTimeout回调函数可以闭包这个新作用域,这个作用域有保存了当时i的值的变量j,因此每一个回调函数可以使用这个j并输出正确结果。
在每次循环中IIFE创建了一个新的作用域,换句话说,我们需要一个基于每次循环的块作用域。let关键字可以拦截一个块,并在此块中声明一个变量,此块就可以变成闭包的作用域,因此下面的代码也可以正确解决问题:
甚至可以直接把let关键字作用于for循环的头部,此时let声明的变量不是在循环中只声明一次,而是在每次循环时都会被声明,而且被初始化为上次循环结束时的值:
for(var i=1;i<=5;i++){ setTimeout(function timer(){ console.log(i); },i*1000); }
最终的结果并不如我们期待的打印出1,2,3,4,5,而是打印出6,6,6,6,6.
setTimeout的回调函数timer会在循环结束后才开始执行,因此会输出i最后的值6.即使将setTimeout的第二个参数设为0,timer还是会在循环结束后才执行,因此还是会打印6.
我们本来期待每一次循环中都能保留当时的i,但当闭包起作用时,虽然每个函数是在各次循环中依次定义的,但它们最后都share同一个作用域,即全局作用域,因此都使用同一个i。五个函数其实只是一个接一个地被声明,其中并没有循环存在了。
如何改进呢?
首先想到使用IIFE(立即执行函数):
for(var i=1;i<=5;i++){ (function(){ setTimeout(function timer(){ console.log(i); },i*1000) })(); }
IIFE的确增加了一层作用域,每次setTimeout执行都会闭包其当前循环中的IIFE作用域,此时的确每个timer函数闭包了自己的作用域,但IIFE是空的,最后导致每一个timer还是会调用同一个全局变量i,值为6.
从空的IIFE进行改进:每个函数都需要它自己的变量,因此要在在每次循环中保存当时的i:
for(var i=1;i<=5;i++){ (function(){ var j = i; setTimeout(function timer(){ console.log(j); },j*1000) })(); }
这回才得出正确的结果!
另一种写法:
for(var i=1;i<=5;i++){ (function(j){ setTimeout(function timer(){ console.log(j); },j*1000) })(i); }
每一次循环中的IIFE为当次循环创建了一个新的作用域,这样其内部的setTimeout回调函数可以闭包这个新作用域,这个作用域有保存了当时i的值的变量j,因此每一个回调函数可以使用这个j并输出正确结果。
在每次循环中IIFE创建了一个新的作用域,换句话说,我们需要一个基于每次循环的块作用域。let关键字可以拦截一个块,并在此块中声明一个变量,此块就可以变成闭包的作用域,因此下面的代码也可以正确解决问题:
for(var i=1;i<=5;i++){ let j=i; //为闭包创建了一个块作用域 setTimeout(function timer(){ console.log(j); },j*1000); }
甚至可以直接把let关键字作用于for循环的头部,此时let声明的变量不是在循环中只声明一次,而是在每次循环时都会被声明,而且被初始化为上次循环结束时的值:
for(let i=1;i<=5;i++){ setTimeout(function timer(){ console.log(i); },i*1000); }
相关文章推荐
- JQuery1——基础($对象,选择器,对象转换)
- Android学习笔记(二九):嵌入浏览器
- Android java 与 javascript互访(相互调用)的方法例子
- 深入理解PHP之匿名函数
- JavaScript演示排序算法
- javascript实现10进制转为N进制数
- 最后一次说说闭包
- Ajax
- 2019年开发人员应该学习的8个JavaScript框架
- HTML中的script标签研究
- 对一个分号引发的错误研究
- 异步流程控制:7 行代码学会 co 模块
- ES6 走马观花(ECMAScript2015 新特性)
- JavaScript拆分字符串时产生空字符的原因
- Canvas 在高清屏下绘制图片变模糊的解决方法
- Redux系列02:一个炒鸡简单的react+redux例子