JS 初学者可能会碰到“变量提升”、“函数声明提升”等术语。在深入讨论任何“提升(hoisting)”的定义之前,先举个例子 -- 定义一个函数并调用:
function cowSays(sound){ console.log(sound);}复制代码
cowSays('moo');复制代码
当调用函数并传入一个自定义的字符串函数时,不出预料的打印出了传入的字符串:
cowSays('moo');// moo复制代码
如果在函数声明之前去调用会怎样呢?
cowSays('moo');复制代码
function cowSays(sound){ console.log(sound);}复制代码
可能出乎所料,'moo' 还是被打印了出来
cowSays('moo');// moo复制代码
这就是提升。
所以,这里发生了什么?通常来说,人们会把提升解释为声明被移动到了代码的顶端。虽然看起来这是正在发生的事情,但是清楚的理解到底是如何才是重要的。
显然,代码没有被移动到任何地方,并没有被神奇的移动到文件的顶端。真正发生的事情是,在编译阶段,函数和变量的声明就被加入内存了。
在上面的例子中,正因为这个原因,才能在其代码出现的位置之前就访问或调用那个函数。
再看看变量的例子:
当声明并初始化一个变量,然后试图使用它时,典型的做法是:
var a = 3;console.log(a);复制代码
// 3复制代码
但是,如果把变量声明放在代码的底部会如何呢?
a = 3;console.log(a);var a;复制代码
// 3复制代码
正如你所见,以上例子打印出了3
。
那么下面这个例子,如果把变量的声明和初始化都放在底部呢?
console.log(a);var a = 3;复制代码
// undefined复制代码
嗯,这个例子第一次出乎我们的预料。预想中的3
没有打印出来,却成了 undefined。
为什么会这样?因为 JS 只会提升声明。而初始化赋值不会被提升。
并且,声明而不赋值时,变量会被自动初始化为 undefined,所以出现了上面的结果。
事实上,以上代码等效于:
var a;console.log(a);a = 3;复制代码
// undefined复制代码
最佳实践:
因为提升的原因,公认的最佳实践是:总是在其作用域的顶端声明变量或函数。这种方法没有不良的作用。
并且,总是应该在定义了变量后就初始化它们,这将提供清晰的代码,并避免 undefined 的出现。