冲突发现
在学习执行上下文的时候我们学到:
- 在函数执行的时候会创建函数的执行上下文
- 执行上下文在创建阶段会确认 变量声明
- 变量声明包括
- 函数的形参(需要明确赋值 没有就为undefined)
- arguments(需要明确赋值 没有就为undefined)
- 字面量函数声明(需要明确赋值)
- var形参(不需要赋值)
也就是说,会有下面的情况
function test(num) {
console.log(num) // 明确赋值的形参
console.log(arguments) // [Arguments] { '0': 1, '1': 2, '2': 3 } argument明确赋值
console.log(inner) // [Function: inner] 明确赋值的函数
console.log(a) // undefined 变量声明,没有赋值,所以为 undefined
function inner() {
console.log('inner')
}
var a = 2
}
test(1, 2, 3)
我们可以看到在函数内部会提前将字面量函数明确赋值,所以可以打印出inner函数
但是会有例外
function test(num) {
console.log(num) // 明确赋值的形参
console.log(arguments) // [Arguments] { '0': 1, '1': 2, '2': 3 } argument明确赋值
console.log(inner) // undefined
console.log(a) // undefined 变量声明,没有赋值,所以为 undefined
if (true) {
function inner() {
console.log('inner')
}
}
var a = 2
}
test(1, 2, 3)
这里如果将函数放到块级作用域之后,打印函数为undefined
块级作用域下函数声明的特殊机制
本质:这个冲突的核心在于传统的执行上下文规则与ES6块级作用域规则之间的矛盾
在反复权衡下,最终以ES6以块级作用域规则为准,但同时为了兼容性做出了复杂的妥协。
ES6后的双重提升机制
如果处于块级作用域,函数声明会提升到块级作用域的顶部和上级作用域顶部
function test() {
console.log(foo); // undefined
if (true) {
console.log(foo); // undefined
function foo() {
return 'block function';
}
console.log(foo); // [Function: foo]
}
console.log(foo); // [Function: foo] - 函数被提升到外层作用域
}
历史下的妥协
在ES6之前,大多数JavaScript引擎将块中的函数声明提升到函数作用域顶部:
// ES5行为
function test() {
if (false) {
function foo() {
return 'never executed';
}
}
console.log(foo); // 在某些引擎中可能是 [Function: foo]
}
为了兼容之前的代码,es6不得不将函数的声明上去,但是不会赋值默认为undefined
最佳实践
不要在块级作用域里声明字面量函数