文章大部分是对javascript高级程序设计的总结。
let声明
块级作用域
let声明的范围是块作用域,而var声明的范围是函数作用域。
let声明的变量不能在if块外部被引用,是因为它的作用域仅限于该块内部。块作用域是函数作用域的子集,因此适用于var的作用域限制同样也适用于let。
if (true) {
let age = 26;
console.log(age); // 26
}
console.log(age); // ReferenceError: age没有定义
冗余声明
let也不允许同一个块作用域中出现冗余声明
var name;
var name;
let age;
let age; // SyntaxError;标识符age已经声明过了
暂时性死区
let声明的变量不会在作用域中被提升。
console.log(age); // ReferenceError:age没有定义
let age = 26;
在let声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone) ,在此阶段引用任何后面才声明的变量都会抛出ReferenceError。
全局声明
let在全局作用域中声明的变量不会成为window对象的属性(var声明的变量则会)。
let age = 26;
console.log(window.age); // undefined
条件声明
let的作用域是块,所以不可能检查前面是否已经使用let声明过同名变量,同时也就不可能在没有声明的情况下声明它。使用try/catch语句或typeof操作符也不能解决,因为条件块中let声明的作用域仅限于该块。
<script>
let name = 'Nicholas';
let age = 36;
</script>
<script>
// 假设脚本不确定页面中是否已经声明了同名变量
// 那它可以假设还没有声明过
if (typeof name === 'undefined') {
let name;
}
// name被限制在if {} 块的作用域内
// 因此这个赋值形同全局赋值
name = 'Matt';
try {
console.log(age); // 如果age没有声明过,则会报错
}
catch(error) {
let age;
}
// age被限制在catch {}块的作用域内
// 因此这个赋值形同全局赋值
age = 26;
</script>
注意 不能使用let进行条件式声明是件好事,因为条件声明是一种反模式,它让程序变得更难理解。如果你发现自己在使用这个模式,那一定有更好的替代方式。
for循环中的let声明
在let出现之前,for循环定义的迭代变量会渗透到循环体外部:
// var
for (var i = 0; i < 5; ++i) {
// 循环逻辑
}
console.log(i); // 5
// let
for (let i = 0; i < 5; ++i) {
// 循环逻辑
}
console.log(i); // ReferenceError: i没有定义
let声明迭代变量时,JavaScript引擎在后台会为每个迭代循环声明一个新的迭代变量。每个setTimeout引用的都是不同的变量实例,所以console.log输出的是我们期望的值,也就是循环执行过程中每个迭代变量的值。
// var
for (var i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0)
}
// 你可能以为会输出0、1、2、3、4
// 实际上会输出5、5、5、5、5
// let
for (let i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0)
}
// 会输出0、1、2、3、4
const声明
const声明变量时必须同时初始化变量,且尝试修改const声明的变量会导致运行时错误。
const age // SyntaxError
const age = 26;
age = 36; // TypeError:给常量赋值
// const也不允许重复声明
const name = 'Matt';
const name = 'Nicholas'; // SyntaxError
// const声明的作用域也是块
const name = 'Matt';
if (true) {
const name = 'Nicholas';
}
console.log(name); // Matt
const声明的限制只适用于它指向的变量的引用。如果是对象修改这个对象内部的属性并不违反const的限制,如果是数组修改这个数组中的元素也不违反const的限制。
const person = {};
person.name = 'Matt'; // ok
const list = [1,2,3]
list[0]=4 // ok
for-of和for-in循环
每次迭代会创建一个新变量
let i = 0;
for (const j = 7; i < 5; ++i) {
console.log(j);
}
// 7, 7, 7, 7, 7
for (const key in {a: 1, b: 2}) {
console.log(key);
}
// a, b
for (const value of [1,2,3,4,5]) {
console.log(value);
}
// 1, 2, 3, 4, 5
总结
let
- let是块级作用域,一个 { }(大括号)就是一个块
- let也不允许同一个块作用域中出现冗余声明
- let声明之前的执行瞬间被称为“暂时性死区”
- let在全局作用域中声明的变量不会成为window对象的属性
- 不要用let做条件声明
- let声明迭代变量时,JavaScript引擎在后台会为每个迭代循环声明一个新的迭代变量。
const
- const声明变量时必须同时初始化变量
- const声明的基础类型变量不能被修改
- const声明的限制只适用于它指向的变量的引用(修改对象内部属性与数组元素不被限制)
- 在for-of和for-in循环中使用(每次迭代都会创建新变量)
最佳实践
- 不使用var
- const优先,let次之