ES6引用了let和const,这实际上带来了块级作用域,不过在ES5之前只存在全局作用域和函数作用域,相信小伙伴对着句话应该不陌生吧,不过下面就来聊聊一个ES5的块级作用域
思考下面一个例子代码输出值为多少
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 100);
}
这里会输出10个10,因为只存在一个全局变量i,我们可以通过立即执行函数来解决这个问题
for (var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function () {
console.log(i);
}, 100);
})(i);
}
如果在ES6环境下可以简单使用let命令,它会在每次循环的时候创建一个作用域,至于为什么能记住上一次的值,那是因为js引擎会记住上一次循环的值。
for (let i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
}, 100);
}
上面的例子只是简单回顾一下没有块级作用域的痛苦之一,实际上我们写项目的时候可能早早的就使用了babel,这里也是后面需要说的,它会通过怎么样的形式将let和const转化为支持ES5的环境。
铺垫了这么久,下面就来说说这个特殊的作用域,看下面一个例子
try {
throw 5;
}catch(e) {
console.log(e);
}
console.log(e); //error e is not defined
这里try会创建一个块级作用域,在全局环境下a不存在,所以报错了。 聪明的小伙伴可能已经想到了,可以使用try来创建块级作用域,理论上是可行的,不过实际上有两点问题
- 性能太慢
- 语法丑陋,要显示的报错
我们来看下babel怎么处理上面的例子
"use strict";
var _loop = function _loop(i) {
setTimeout(function() {
console.log(i);
}, 100);
};
for (var i = 0; i < 10; i++) {
_loop(i);
}
可以看到与立即执行函数基本相同,不过使用立即执行函数创建块级作用域需要注意一点this的指向
例如这个例子
const obj = {
foo() {
for (let i = 0; i < 5; i += 1) {
setTimeout(() => {
console.log(this.arr[i]);
}, 1000);
}
},
arr: [1, 2, 3, 4, 5],
};
箭头函数没有自己的this对象,是定义时的对象而不是运行时的对象,在这个例子中指向的是obj; 如果要把上面的代码转化为ES5环境支持的代码,我们可以通过作用域的规则来实现
(function() {
var _this = this;
setTimeout(function() {
console.log(_this.arr[i]);
}, 1000);
})();
上面就实现了所需的功能