考虑以下两段代码:

1
2
3
4
5
6
7
let x = 1,
z = 2

function foo(w = x + 1, y = w + 1, z = z + 1) {
console.log(x, y, z)
}
foo() // ReferenceError: z is not defined
1
2
3
4
5
6
7
let x = 1,
z = 2

function foo(w = x + 1, y = w + 1, z) {
console.log(x, y, z)
}
foo() // 1 3 undefined

第一段会报错:ReferenceError: z is not defined

第二段不报错,输出:1 3 undefined

然后就很不解,ES6 的函数的参数在进行默认值赋值时是基于一种怎样的规则?对第一个 w 赋值,找 x,也是找的其上层作用域的 x,为什么找 z 的时候就报错了,z 明明在其上层作用域也存在!为了找到原因,用 babel 将其编译,编译后的结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
"use strict";

var x = 1,
z = 2;

function foo() {
var w = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : x + 1;
var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : w + 1;
var z = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : z + 1;

console.log(x, y, z);
}
foo();

想到了 ES5 的函数和变量的声明提升,上述代码会被引擎提前处理成如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var x,
z;
x = 1
z = 2;

function foo() {
var w,
y,
z;

w = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : x + 1;
y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : w + 1;
z = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : z + 1;

console.log(x, y, z);
}
foo();

编译器对 w 进行 LHS 查询时,会对 x 进行 RHS 查询,从当前作用域开始一直往上查找,当前作用域中没有 x,上层作用域中 x=1,即 w=2

编译器对 y 进行 LHS 查询时,会对 w 进行 RHS 查询,在当前作用域中找到了 w=2,所以 y=3

编译器对 z 进行 LHS 查询时,会对 z 进行 RHS 查询,虽然上层作用域中有 z,但还是会从当前作用域开始一直往上查找,当前作用域中有 z,z 虽然声明但未进行赋值,即 z===undefined

遇到 ES6 中不理解的语法糖时,一定要先将其转成熟悉的 ES5,了解问题背后的真相和本质