还是先从一个题目开始:
如果可以用ES6语法,则可以这么写:
function print (arr) {
for (let i = 0; i < arr.length; i++) {
setTimeout(() => {
console.log(arr[i])
}, 1000 * i);
}
}
但是如果把这里的let
改成var
,则输出就会变成一连串的undefined
。
有同学很快想到了这是闭包啊,因为setTimeout
把函数加入到microqueue
中,所以等到setTimeout的函数体执行时,i
已经走完了for
循环,变成了arr.length
。arr[arr.length]
显然是undefined。
简单修改一下,变成ES5的语法。
function print (arr) {
for (var i = 0; i < arr.length; i++) {
(function (index) {
setTimeout(() => {
console.log(arr[index])
}, 1000 * index);
})(i);
}
}
其实就是利用闭包是向父级作用域寻找值的特性,给i
包装一层作用域,把i
存起来。
闭包概念还请翻看之前的一篇blog-闭包和类。
到这里闭包的理解应该差不多了,而今天的关键点在于——
let做了什么?
阮一峰老师的《ECMAScript 6》入门里给出[定义](http://es6.ruanyifeng.com/#docs/let:
他提到了let的几个特性:
只存在于块级作用域中
不存在变量提升
暂时性死区
不允许重复声明
这里我不再赘述,大家可以仔细阅读一下阮一峰老师的书。
我更感兴趣的是,在ES5的语法中,如何模拟let
这种块级作用域的效果。这个时候,应该让babel
出场了。
打开这个:可以看到转换后的代码。
"use strict";
function print(arr) {
var _loop = function _loop(i) {
setTimeout(function () {
console.log(arr[i]);
}, 1000 * i);
};
for (var i = 0; i < arr.length; i++) {
_loop(i);
}
}
其实可以对比发现,babel
转换后的代码和我们上面写的ES5实现其实是一样的。
大概就是通过对let
绑定的块级作用域加一个函数,把let
声明的参数,通过函数传入,达到块级作用域的效果。
大家可以在babel
试一下let
的其他特性,转移出的ES5语法并不能实现有的特性,比如暂时性死区。
完,感谢阅读。