[Medium] 📄 Hoisting
1. What's Hoisting ?
JS 的运行可以拆解为两个阶段,分别是创建与执行:
var name = 'Pitt';
console.log(name); // print Pitt
在 Hoisting 特性下,上面这段代码实际运作上,需要理解为先声明变量再执行赋值。
// create
var name;
// execute
name = 'Pitt';
console.log(name);
而 function 又和变量不同,在创建阶段就会指给内存,声明式如下:
getName();
function getName() {
console.log('string'); // print string
}
上面这段之所以能正常运行并打印出 console.log,而不会报错,在于以下逻辑,function 先被提升到最上方,接着才做调用 function 的动作。
// create
function getName() {
console.log('string');
}
// execute
getName();
但需要注意的是,这种 Hoisting 特性,在表达式时需要注意书写顺序。
在创建阶段时,function 是最优先的,其次才是变量。
Correct
name = 'Yumy';
console.log(name); // print Yumy
var name;
// --- Equal to ---
// create
var name;
// execute
name = 'Yumy';
console.log(name); // print Yumy
Wrong
console.log(name); // print undefined
var name = 'Jane';
// --- Equal to ---
// create
var name;
// execute
console.log(name); // print undefined,因为还未拿到赋值,只拿到默认的 undefined
name = 'Pitt';
2. What's name printed ?
whoseName();
function whoseName() {
if (name) {
name = 'Nini';
}
}
var name = 'Pitt';
console.log(name);
Answer
// create
function whoseName() {
if (name) {
name = 'Nini';
}
}
var name;
// execute
whoseName();
name = 'Pitt';
console.log(name); // print Pitt
name 在 whoseName() 中,虽然因为拿到 undefined,不会往下走判断。
但因为声明式的下方,又有一个赋值,所以即使在 function 有进入判断条件,最终仍会打印出 Pitt。
3. 函数声明 vs 变量声明:提升优先级
题目:同名的函数和变量
试判断以下代码的输出结果:
console.log(foo);
var foo = '1';
function foo() {}
错误答案(常见误解)
很多人会以为:
- 输出
undefined(认为 var 先提升) - 输出
'1'(认为赋值会影响) - 报错(认为同名冲突)
实际输出
[Function: foo]
为什么?
这题考察 Hoisting 的优先级规则:
提升优先级:函数声明 > 变量声明
// 原始程式码
console.log(foo);
var foo = '1';
function foo() {}
// 等价于(经过 Hoisting)
// 阶段 1:创造阶段(Hoisting)
function foo() {} // 1. 函数声明先提升
var foo; // 2. 变量声明提升(但不覆盖已存在的函数)
// 阶段 2:执行阶段
console.log(foo); // 此时 foo 是函数,输出 [Function: foo]
foo = '1'; // 3. 变量赋值(会覆盖函数)
关键概念
1. 函数声明会完整提升
console.log(myFunc); // [Function: myFunc]
function myFunc() {
return 'Hello';
}
2. var 变量声明只提升声明,不提升赋值
console.log(myVar); // undefined
var myVar = 'Hello';
3. 当函数声明和变量声明同名时
// 提升后的顺序
function foo() {} // 函数先提升并赋值
var foo; // 变量声明提升,但不会覆盖已存在的函数
// 因此 foo 是函数
console.log(foo); // [Function: foo]