我觉得原 PO 的问题可能不是 hoisting,而是不知道 test = ... 会把 function test
覆蓋掉。不过这篇还是讲 hoisting。
Hoisting 这个“行为”是被人“观察”出来的。以 C 为例子︰
#include <stdio.h>
int a = 1;
int main() {
printf("%d\n", a);
int a = 2;
printf("%d\n", a);
}
得到的结果会是
1
2
但在 JavaScript 中
var a = 1;
function main() {
console.log(a);
var a = 2;
console.log(a);
}
main();
得到的结果是
undefined
2
人们就给出一套解释︰“以 var 定义的变量,会被提到 function scope[1] 的最上面,
而且初始值是 undefined。并且会等到原本的位置(a = 2)才会赋值”,称为
“Hoisting”。(其实这个说法是错的,在定义 var 时并所谓的“初始值”并不一定是
undefined,可能该变量本来就在 scope 内,或是被 function arguments 赋值)
[1]: 关于 scope: https://is.gd/tcHqDy
事实上 JS 做的事情是︰在进入一个新的 scope 的时候,会把 var, function,
const, let 的变量收集起来,并且把变量名称注册到目前的 scope 中,接着才开始执行
接下来的程式码。
它们之前的差别︰
var 是丢到 function scope,并且可以重复定义,在未赋值前存取会得到 undefined。
function 也是丢到 function scope,可以重复定义。若定义是放在 scope 的最外层
(定义位于 function scope 底下,而且不在任何 block scope 内),则会提前赋值,
所以你可以在 function 定义前就使用这个变量。否则就和 var 相同,在未赋值前存
取会得到 undefined。
const 和 let 是丢到 block scope,不能重复定义,在还未赋值前存取会
ReferenceError。
const 还有一个限制是不能重复赋值。
至于 const, let 也有 hoisting 现象,解释为“将定义提升到 block scope 的最上
方,但若提前存取会丢出 ReferenceError”,并使用 temporal dead zone 的概念。
要解释这个的话又和 function 的 default parameter 或 for, while 有关……
这里解释得满清楚的︰
http://exploringjs.com/es6/ch_variables.html
如果看了这一堆还很有兴趣的话,可以玩这个︰
http://perfectionkills.com/javascript-quiz-es6/