@Dale-Lin
2018-12-02T22:46:41.000000Z
字数 2829
阅读 897
深入理解ES6
ES6引入块级作用域来强化变量生命周期的控制
ES6块级作用于存在于:
let声明用法和var相同,但可以把变量的作用域限制在当前代码块中(不会被提升)。
通常在封闭代码块的顶部使用let声明。
若块级作用域中已存在某个标识符,再使用let声明它会抛出语法错误:
var count = 30;
//throw a syntax error
let count = 40;
但在某作用域的内嵌作用域中,可以使用let声明同名变量:
var count = 30;
if (condition){
let count = 40;
}
const关键字声明的是常量,其值一旦设定后不可更改。
使用const声明的常量必须进行初始化。
const maxItem = 30;
//throw a syntax error
const name;
//throw a syntax error
maxItem = 20;
在同一作用域中用const声明已存在的标识符也会导致语法错误。
var message = 'Hello~';
let age = 18;
//both two will throw an error
const message = 'goodbye!'
const age = 19;
const 和 let 声明的都是块级标识符,只在当前代码块中有效,且不会被提升。
if (condition){
const maxItem = 5;
}
//can't access maxItem here
const声明不允许修改绑定,但是可以修改值。意味着在声明对象后,可以修改该对象的属性值:
const person = {
name: 'Nicholas'
};
//can change prop's value
person.name = 'Greg';
由于let/const声明的变量不会像var声明的那样提升,如果在声明之前访问这些变量,即便是相对安全的typeof操作符也会触发引用错误:
if (condition){
console.log(typeof value); //引用错误
let value = 'blue';
}
使用console.log的操作会触发错误,故使用let初始化变量value的语句不会执行。此时value就位于“临时死区”(temporal dead zone)或TDZ中。换成const也一样。
原因是JS引擎扫描发现变量的时候,要么将其提升到作用域顶部(var声明),要么将声明放在TDZ中(let/const声明)。访问TDZ中的变量会触发运行时错误,只有执行过变量声明语句后,变量才会从TDZ中移出,然后方可正常访问。
但在let声明的作用域外使用typeof操作符不会报错,因为并不在这个TDZ中:
console.log(typeof value); //undefined
if (condition) {
let value = 'blue';
}
在for循环中使用let声明可以防止循环后变量i在其他地方被访问:
for (let i = 0; i < 10; i++) {
process(item[i]);
}
console.log(i); //不可访问,抛出错误
var声明使得在循环中创建函数变得异常困难,因为变量到了循环之外仍能访问:
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
//输出10次10
funcs.forEach(function(func) {
func();
}};
ES5使用立即调用函数来处理问题:
var funcs = [];
for (var i = 0; i < 10; i++){
funcs.push((function(value){
return function() {
console.log(value);
};
}(i)));
}
funcs.forEach(function(func){func();});
原理是用一个变量value储存每次i的副本。
ES6的let和const提供的块级绑定让函数中的循环可以预期运行。
let声明模仿上述例子中IIFE来简化循环过程,每次迭代循环会创建一个新变量,并以之前迭代中同名变量的值将其初始化:
var funcs = [];
for (let i = 0; i < 10; i++){
funcs.push(function(){
console.log(i);
})
}
funcs.forEach(function(func) {
func(); //0,1,2,3,4,5,6,7,8,9
})
每次循环,let声明都会创建一个新变量i,并将其初始化为i的当前值。
对于for-in循环、for-of循环也是一样的:
var funcs = [],
object = {
a: true,
b: true,
c: true
};
for (let key in object) {
funcs.push(function(){
console.log(key);
})
}
funcs.forEach(function(func){
func(); //a, b, c
});
注意const声明的变量不能被修改,否则会抛出错误,常规的for循环会发生这个问题。
在for-in或for-of循环中使用const时的行为和let一样:
var funcs = [],
object = {
a: true,
b: true,
c: true
};
for (const key in object){
funcs.push(function(){
console.log(key);
});
}
funcs.forEach(function(func){
func(); //a, b, c
});
循环内不改变key的值不会触发错误,因为在for-in或for-of循环中,每次迭代不会修改已有绑定,而是会创建一个
在全局作用域下,用var声明的变量会作为全局对象(浏览器中为window对象)的属性。
但在浏览器全局作用域下,使用let/const声明的变量不会成为window的属性,但window对象的属性会出现在全局作用域中:
let a = 'bbb';
const b = 'ccc';
window.a; //undefined
window.b; //undefined
'a' in window; //false
window.a = 'bbb';
//抛出语法错误!
let a = 'ccc';
用let/const创建了一个绑定并遮蔽了全局的变量,但不会破坏全局作用域。
如果不想为全局对象创建属性,则使用let或const代替var会安全得多。
如果希望在全局对象下定义变量,仍然可以使用var。这种情况常用于在浏览器中跨frame或跨window访问代码。