Javascript Hoisting

编程过程中,别人谈到Javascript中的funcation是顺序执行的,所以书写的顺序要留心,不然会出未定义就执行的错误。当然,C语言出身的程序员不以为意,会认为理所当然,C语言就是这么干的。但Javascript并非如此!

ints.js
1
2
3
4
5
6
7
8
9
10
function ints(arrOfNumberStr) {
return arrOfNumberStr.map(toInt);

function toInt(str) {
return parseInt(str, 10);
}
}

ints(['11', '11', '11']);
-> [11, 11, 11]

从执行结果上来看,先调用后执行时正确的。但是JS是解析执行的,如果没有预先定义,执行到调用处必然会失败,那么为什么这段代码可以工作呢?

先看一段略做修改之后的代码

ints_fail.js
1
2
3
4
5
6
7
8
9
10
function ints(arrOfNumberStr) {
return arrOfNumberStr.map(toInt);

var toInt = function(str) {
return parseInt(str, 10);
}
}

ints(['11', '11', '11']);
-> TypeError: undefined is not a function

执行结果失败了。回头看这两段代码的定义,不难发现程序对于toInt的定义方式有差别,一种用常规的函数定义方式,一种使用var定义。

接着再看一段代码

variables.js
1
2
3
4
5
6
7
8
function loop(n) {
for(var i=0; i<n; i++);

return i;
}

loop(10);
-> 10

估计学习C语言的人开始惊讶了:这个i的作用域不是应该只在for loop中吗?

那么到底是什么原因造成的呢?这就是Javascript Hoisting!简单解释,声明提升到作用域的最前面。

接上面的variable.js

variables_hoisting.js
1
2
3
4
5
6
function loop(n) {
var i;
for(i=0; i<n; i++);

return i;
}

这和原来的写法是等价的,所以i的作用域在整个function中都是有效的。依次类推,init_fail.js中的var toInt这样的函数声明也会被提升到作用域的最上方,但是只是声明被提升了,真正赋值还是在后面,所以此时调用该函数,它还是undefined

但是对于init.js而言,它的声明方式会将整个函数都提升,所以它是先于调用方赋值初始化的,所以能工作。

‘use strict’ 可以防止 only reference hoisting?


如果变量未声明,use strict模式下赋值会报错。

use_strict.js
1
2
3
4
5
(function(){
'use strict';
var foo = 123;//works fine
bar = 345;//ReferenceError: bar is not defined
}());

如果在以前例子中使用use strict,并不能提前报出toInt未赋值的错误。直到运行期间,才会发现toInt依然undefined

use_strict_for_hoisting.js
1
2
3
4
5
6
7
8
(function ints(arrOfNumberStr) {
'use strict';
return arrOfNumberStr.map(toInt);
var toInt = function(str) {
return parseInt(str, 10);
}
})(['11', '11']);
-> Uncaught TypeError: undefined is not a function()

优先使用function declaration


Why? Function declarations are named, so they’re easier to identify in call stacks. Also, the whole body of a function declaration is hoisted, whereas only the reference of a function expression is hoisted. This rule makes it possible to always use Arrow Functions in place of function expressions. – from javascript style guide

下篇会记录Javascript的作用域Global Scope, Lexical Scope, Dynamic Scope, Function Scope