首先,JavaScript代码在执行时是从上到下一行一行执行的这种说法并不十分正确。
先看两个例子:
a = 2
var a
console.log(a)
上面的例子的输出结果为2
console.log(a)
var a = 2
上面的例子的输出结果为undefined
分析:引擎在运行JavaScript代码之前会先对其进行编译工作。而编译过程的一部分工作就是找到所有的声明,并用合适的作用域来安置他们。因此,分析代码的正确思路是:包括变量和函数在内的所有声明都会在任何代码运行前首先被处理。上面所说的这种现象就是我们称作的“声明提升”现象。
1.函数声明和函数表达式的声明情况
函数声明是会被正确提升的,但是对于函数表达式的声明则会不尽人意,当然这也是合理之中的设计。
foo()//it's okay
function foo(){console.log(1)}
fun()//TypeError
var fun = function(){console.log(1)}
分析:对于函数表达式的提升而言,首先函数变量会被提升,但是他的类型却是不可知的,因为类型必须根据等号右边才能够推断的出来,但是等号右边的内容那是运行时才能知道的,所以对于函数表达式的声明提升而言,函数变量被提升为了undefined,如果你对函数变量进行调用操作的话,则会发生TypeError错误。而非ReferenceError错误。
再看一下下面这种例子:
fun()//TypeError
bar()//ReferenceError
var fun = function bar(){...}
还有就是得格外注意这种情况:
var foo = function bar(){...}
bar()//ReferenceError
对于上面这种情况,记住就好。
2.函数优先
前面提到过,不论是函数声明还是变量声明都会得到声明提升,但是一个值得注意的地方是如果定义了一个同名函数变量和一个普通变量的话,而且此时都是声明提升的话,那么无论函数声明的位置是在变量之前还是之后,结果都是在查找这个变量时函数被优先考虑举个例子:
foo()
function foo(){console.log(1)}
foo = 1
var foo
上面的输出结果并不是ReferenceError,而是输出了1。对于这种情况,感觉解释时解释不通的。所以需要记住的就是:** 当同名的函数变量和普通变量都声明提升时,那么当在他们两者的声明之前查询该变量的时候,查询得到的结果将一定是函数,无论声明位置的先后顺序 **。
当然,如果不是在两者的声明之前调用的话,那么就不会出现这种情况:
function fun(){...}
fun = 1
var fun
fun()//TypeError,fun is not a function