我们都知道,变量在使用之前必须声明。但我们在 javascript
中可能见到这样的代码。
1 | console.log(a); |
这着实令我们费解。当然事出必有因,这也引出了我们今天要讨论的内容:变量提升 (Hoisting)
。
变量提升
在 ES6
之前,javascript 的作用域一共两种:全局作用域
和方法作用域
。
1 | // 这里是全局作用域 |
不难看出,各个作用域之间声明的变量相互独立,互不影响。上面的代码中我们对 f4()
感到疑惑。a
不是应该使用全局作用域的 a
, 输出为:'全局的 a'
吗?
这就是之前提到的 变量提升
,javascript 中,每一个变量的声明都被提升到所在作用域的顶部,不过初始化顺序不变。
那么,对于 f4()
方法而言:var a = 'f4 的 a';
等同于 var a; a = 'f4 的 a';
。所以整个 f4()
方法就等同于以下代码:
1 | (function f4() { |
这样就豁然开朗了,我们开始时的疑问解决了。不得不说,这样的设计在调试程序时确实很糟糕。
随着 ES6
的到了,引入了两个新的关键字 let
和 const
。const
和其他语言并无差,用于声明常量,一旦声明,不能修改。
let
和 var
类似(ES6
已不再推荐使用 var
),但抛弃了 var
的“不合理”行为。
- 同一作用域内不能多次声明同一变量。
- 不再出现变量提升。
ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
1 | // 同一作用域内不能多次声明同一变量。 |
函数提升
你可能也猜到了,既然由 变量提升
,那肯定也会有 函数提升
吧。
是的,我们也来看看它的表现:
1 | fn0('Hello ~'); // Hello ~ |
不难发现,fn1()
被提到了顶部。然后,被第一个方法“覆盖”。
关于函数
javascript 中函数的声明我们一般采用下面两种方式:
1 | // 第一种 |
这两种方式基本等同。不过第二种会出现 函数提升
,而第一种则不会。
对于 javascript 而言,函数其实是 Function
对象的实例。
1 | var fn = function(word) { |