ES2015
一、块级绑定
1、let、const和块级作用域
var 变量提升(hoisting )
使用var关键字声明的变量,无论其实际声明位置在何处,都会被视为声明于所在函数的顶部(如果声明不在任意函数内,则视为在全局作用域的顶部)。
function getValue(condition) {
//value声明被提升到此处
if (condition) {
var value = "blue";
// 其他代码
return value;
} else {
// value 在此处可访问,值为 undefined
return null;
}
// value 在此处可访问,值为 undefined
}
let声明
let 声明的语法与 var 的语法一致,但会将变量的作用域限制在当前代码块中。
function getValue(condition) {
if (condition) {
let value = "blue";
// 其他代码
return value;
} else {
// value 在此处不可用
return null;
}
// value 在此处不可用
}
禁止重复声明
如果一个标识符已经在代码块内部被定义,那么在此代码块内使用同一个标识符进行 let 声明就会导致抛出错误。
var count = 30;
let count = 40;// 语法错误
if (condition) {
let count = 40;// 不会抛出错误
// 其他代码
}
常量声明
所有的const变量都需要在声明时进行初始化,常量声明与 let 声明一样,都是块级声明。
const maxItems = 30;// 有效的常量
const name;// 语法错误:未进行初始化
常量如果是一个对象,它所包含的值是可以被修改的。
const person = {
name: "Nicholas"
};
person.name = "Greg";// 工作正常
person = {// 抛出错误
name: "Greg"
};
暂时性死区( temporal dead zone , TDZ )
console.log(typeof value); // "undefined"
if (condition) {
console.log(typeof value); // 引用错误
let value = "blue";
}
循环内的函数
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push(function() { console.log(i); });
}
funcs.forEach(function(func) {
func(); // 输出数值 "10" 十次
});
强制修正
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push((function(value) {
return function() {
console.log(value);
}
}(i)));
}
funcs.forEach(function(func) {
func(); // 从 0 到 9 依次输出
});
循环内的 let 声明
这种方式在 for-in和 for-of 循环中同样适用(每次循环都创建一个副本,const同理)
var funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs.forEach(function(func) {
func(); // 从 0 到 9 依次输出
})
需要重点了解的是: let 声明在循环内部的行为是在规范中特别定义的,而与不提升变量声明的特征没有必然联系。事实上,在早期 let 的实现中并没有这种行为,它是后来才添加的。
循环内的 const 声明
var funcs = [];
for (const i = 0; i < 10; i++) {//循环第二次抛出错误
funcs.push(function() {
console.log(i);
});
}
funcs.forEach(function(func) {
func();
})
var funcs = [],
object = {
a: true,
b: true,
c: true
};
for (const key in object) { // 不会导致错误
funcs.push(function() {//循环内部,key不能被修改
console.log(key);
});
}
funcs.forEach(function(func) {
func(); // 依次输出 "a"、 "b"、 "c"
});
const 和 let 情况相同,但是const在for循环中是不能被改变的,所有会抛出错误,for-in for-of是创建副本不是改变const,所以不会抛出错误,但是在循环内部,同样是不能被改变的
全局块级绑定
var 声明全局变量会覆盖全局属性
var RegExp = "Hello!";
console.log(window.RegExp); // "Hello!"
const let不会覆盖全局属性
// 在浏览器中
let RegExp = "Hello!";
console.log(RegExp); // "Hello!"
console.log(window.RegExp === RegExp); // false
若想让代码能从全局对象中被访问,你仍然需要使用 var 。在浏览器中跨越帧或窗口去访问代码时,这种做法非常普遍。
总结
let 与 const 块级绑定将词法作用域引入了 JS 。这两种声明方式都不会进行提升,并且只会在声明它们的代码块内部存在。由于变量能够在必要位置被准确声明,其表现更加接近其他语言,并且能减少无心错误的产生。作为一个副作用,你不能在变量声明位置之前访问它们,即便使用的是 typeof 这样的安全运算符。由于块级绑定存在暂时性死区( TDZ ),试图在声明位置之前访问它就会导致错误。
let 与 const 的表现在很多情况下都相似于 var ,然而在循环中就不是这样。在 for-in与 for-of 循环中, let 与 const 都能在每一次迭代时创建一个新的绑定,这意味着在循环体内创建的函数可以使用当前迭代所绑定的循环变量值(而不是像使用 var 那样,统一使用循环结束时的变量值)。这一点在 for 循环中使用 let 声明时也成立,不过在 for 循环中使用 const 声明则会导致错误。
块级绑定当前的最佳实践就是:在默认情况下使用 const ,而只在你知道变量值需要被更改的情况下才使用 let 。这在代码中能确保基本层次的不可变性,有助于防止某些类型的错误。