深入理解 You Don't Know JS 中的 ES6 块级作用域与扩展运算符
前言
在 JavaScript 的发展历程中,ES6(ECMAScript 2015)无疑是一个里程碑式的版本。它引入了许多新特性,极大地改善了 JavaScript 的编程体验。本文将重点剖析 ES6 中两个重要特性:块级作用域声明(let/const)和扩展/剩余运算符(...),这些内容源自《You Don't Know JS》系列中关于 ES6 的章节。
块级作用域声明
从 var 到 let 的演进
传统 JavaScript 中,变量声明使用 var
,其作用域仅限于函数或全局范围。为了创建块级作用域,开发者通常需要使用立即执行函数表达式(IIFE):
var a = 2;
(function IIFE(){
var a = 3;
console.log(a); // 3
})();
console.log(a); // 2
ES6 引入了 let
关键字,允许我们在任何代码块(由 {}
界定)中声明变量:
var a = 2;
{
let a = 3;
console.log(a); // 3
}
console.log(a); // 2
let 的最佳实践
- 显式块声明:建议使用独立的
{}
块来明确界定作用域 - 声明位置:将
let
声明放在块的最顶部 - 单一声明:一个块中多个变量声明尽量使用一个
let
{ let a = 2, b, c;
// ...
}
let 的隐式特性
let
的隐式特性可能导致一些认知负担:
let a = 2;
if (a > 1) {
let b = a * 3;
console.log(b); // 6
for (let i = a; i <= b; i++) {
let j = i + 10;
console.log(j);
}
// 输出: 12 13 14 15 16
let c = a + b;
console.log(c); // 8
}
在这个例子中,if
块包含 b
和 c
,for
循环包含 i
和 j
。这种隐式绑定需要开发者特别注意。
暂时性死区(TDZ)
let
声明存在暂时性死区(Temporal Dead Zone),即在声明前访问变量会抛出错误:
{
console.log(a); // undefined
console.log(b); // ReferenceError!
var a;
let b;
}
let 在循环中的特殊行为
let
在 for
循环头中会为每次迭代创建新的绑定:
var funcs = [];
for (let i = 0; i < 5; i++) {
funcs.push(function(){
console.log(i);
});
}
funcs[3](); // 3
这与 var
的行为不同,后者会在闭包中共享同一个变量。
const 声明
基本用法
const
用于声明常量,必须在声明时初始化且不能重新赋值:
{
const a = 2;
console.log(a); // 2
a = 3; // TypeError!
}
关于值的不变性
需要注意的是,const
保证的是绑定不变,而非值不变。对于对象和数组,内容仍可修改:
{
const a = [1,2,3];
a.push(4);
console.log(a); // [1,2,3,4]
a = 42; // TypeError!
}
使用建议
- 使用
const
明确表示不应改变的变量 - 不要过度依赖
const
的性能优化假设 - 仅在确实需要不变绑定时使用
const
块级作用域函数
ES6 规定,函数声明在块内具有块级作用域:
{
foo(); // 正常工作
function foo() {
// ...
}
}
foo(); // ReferenceError
这与之前的行为不同,可能影响现有代码:
if (something) {
function foo() { console.log("1"); }
}
else {
function foo() { console.log("2"); }
}
foo(); // ES6 中会抛出 ReferenceError
扩展/剩余运算符(...)
扩展运算符
用于展开可迭代对象:
function foo(x,y,z) {
console.log(x, y, z);
}
foo(...[1,2,3]); // 1 2 3
替代旧的 apply
用法:
// 旧方式
foo.apply(null, [1,2,3]);
// 新方式
foo(...[1,2,3]);
也可用于数组合并:
var a = [2,3,4];
var b = [1, ...a, 5];
console.log(b); // [1,2,3,4,5]
剩余参数
用于收集剩余参数为数组:
function foo(x, y, ...z) {
console.log(x, y, z);
}
foo(1, 2, 3, 4, 5); // 1 2 [3,4,5]
完全替代 arguments
对象:
// ES6 方式
function foo(...args) {
// args 已经是真正的数组
args.shift();
console.log(...args);
}
// 旧方式
function bar() {
var args = Array.prototype.slice.call(arguments);
args.push(4, 5);
args = args.filter(v => v % 2 == 0);
foo.apply(null, args);
}
bar(0, 1, 2, 3); // 输出: 2 4
总结
ES6 的块级作用域和扩展运算符极大地提升了 JavaScript 的表达能力:
let
/const
提供了真正的块级作用域,解决了var
的变量提升问题const
有助于表达不可变意图,但要注意其仅保证绑定不变- 扩展运算符(
...
)简化了数组/参数操作,使代码更简洁 - 剩余参数提供了比
arguments
更优雅的参数处理方式
理解这些特性的工作原理和最佳实践,将帮助你编写更清晰、更健壮的 ES6 代码。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考