变量提升与函数提升
先来看一个例子:
console.log(a);
var a = 10;
直觉上来说,变量a在未被声明前就使用,会报ReferenceError引用错误,表示该变量未被声明,但是在这里,浏览器控制台输出的结果是undefined。
其实在js编译的时候,上面的代码顺序会变成下面这种:
var a;
console.log(a);
a = 10;
这种把变量的声明和赋值分开,并把声明部分提升到作用域顶部的现象,就被称作变量提升。
提升(hoisting)
能被提升的不止变量声明,还有函数声明:
foo(); // 1
function foo(){
console.log(1);
}
在上面的例子中,函数foo的声明在调用之后,但是处于上面的函数调用foo()却也可以正常执行,这说明了函数的声明也别提升了,并且函数的内部代码块也跟着提升上去了。
函数的定义有两种形式:函数声明式和函数表达式,函数声明式会把整个代码块提升上去,但是函数表达式就不一样了:
foo(); // TypeError
var foo = function (){
console.log(1);
}
这里报的是TypeError类型错误而不是ReferenceError引用错误,说明还是存在提升的,但是和函数声明式的提升又不一样,只提升了声明部分,后面的赋值部分没有提升,所以函数表达式的提升过程和变量的提升过程是一样的,因为函数表达式的形式其实就是给变量赋值一个函数类型的数据,本质还是变量的声明赋值过程。
上述代码提升后会变为以下形式:
var foo;
foo();
foo = function (){
console.log(1);
}
在if语句中,使用var声明的变量,也会被提升到if所在作用域的顶部:
console.log(a); // undefined
if (false) {
var a = 1;
}
这里是undefined而不是ReferenceError,说明了即使if语句的条件为false,变量a还是被提升到了if外面的顶部。
对于if语句里的函数声明式,又有些奇怪了:
foo(); // TypeError
if (false) {
function foo(){
console.log(1);
}
}
这里结果是TypeError而不是ReferenceError,说明存在提升的现象,但是又和上面的函数提升不一样,个人认为提升结果可能是下面这种形式:
var foo;
foo();
if (false) {
function foo(){
console.log(1);
}
}
优先级
既然变量和函数都能提升,那么这里面有没有优先级呢?来看看下面这个例子:
console.log(foo); // function...
var foo = 123;
function foo() {
}
这个例子可以看出,存在同名的变量和函数的时候,函数声明会被提升到更前的位置,这导致后面的变量声明被忽略掉:
function foo() {
}
// var foo; // 因为foo声明过了,所以这个声明被忽略了
console.log(foo);
foo = 123;
如果是两个同名的函数,那么后面的声明就会把前面的声明覆盖掉。
foo(); // 2
function foo(){
console.log(1);
}
function foo(){
console.log(2);
}
上面提升后会变成下面这种形式:
function foo(){
console.log(1);
}
function foo(){
console.log(2);
}
foo();
总结
-
使用var声明的变量和函数声明式都会有提升现象
-
提升的位置是所在作用域的顶部
-
if语句中也存在提升现象
-
函数声明的优先级高于变量声明