由于JS编译器的作用,函数声明和变量声明都会被提升,但是一个值得注意的细节是函数会首先被提升,然后才是变量。
提升
变量和函数声明从它们在代码中出现的位置被“移动”到了最上面,这叫变量的提升。分为两个部分,第一部分是将所有的变量声明和函数声明放在了代码的最上方,第二部分的代码运行到指定位置时再执行。
变量的提升
console.log(a); var a = 3; | 等价于 | var a; console.log(a); a = 3; |
函数的提升
foo(); function foo(){ console.log(a); var a=2; } | 等价于 | function foo(){ var a; console.log(a); a=2; } foo(); |
** 注意: **
函数声明会整个被提升,函数表达式不会整个被提升。
foo(); //会报错,但是是TypeError而不是ReferenceError
var foo = function bar(){
//代码
}
因为其中的函数表达式被当成了变量来进行提升,所以上述代码等价于
var foo;
foo();
foo=function bar(){
//代码
}
所以再执行foo函数的时候,能找到foo这个变量,只是没有被赋值,所以是TypeError
#练习(验证开头提出的话)
知识点
var a=2;
var a;
console.log(a);
结果:2
处理上述程序会分为两个部分,一个部分是由编译器在编译时处理,另一部分则由引擎在运行时处理。
编译器做如下处理:
- 遇到var a,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。
如果是,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为a - 接下来编译器会为引擎生成运行时所需的代码,这些代码被用来处理a=2这个赋值操作。
##例子
-
实例1
foo();
var foo;
function foo(){
console.log(1);
}
foo=function(){
console.log(2);
}
结果:1
因为上述代码会被引擎理解为如下形式
function foo(){ //函数首先会被提升
console.log(1);
}
var foo; //接着提升变量
foo(); //1
foo=function(){
console.log(2);
}
-
实例2
foo();
function foo(){
console.log(1);
}
var foo = function(){
console.log(2);
}
function foo(){
console.log(3);
}
结果:3
JS中没有函数重载的概念,同名函数或变量会相互覆盖
-
实例3
var foo = function(){
console.log(1);
}
function foo(){
console.log(2);
}
foo();
结果:1
首先会提升函数声明,接着再提升变量声明
-
实例4
foo();
var foo = 0;
function foo(){
console.log(1);
}
foo();
foo = function(){
console.log(2);
};
foo();
结果:1
报错 TypeError
-
实例5
foo();
var a = true;
if(a){
function foo(){console.log('a');}
}else{
function foo(){console.log('b');}
}
答案:‘b’
因为JS并不是以代码段为作用域,而是以函数为作用域的。所以上述代码会被引擎理解为如下形式:
function foo(){
console.log('a');
}
function foo(){
console.log('b');
}
foo();
var a = true;
if(a){
foo();
} else {
foo();
}
-
实例6
var a=10;
function fn(){
if (!a) {
var a=20
}
console.log(a)
}
fn()
答案:20
-
实例7
var x=foo();
var foo=function () {
return 2;
};
console.log(x);
答案:TypeError: foo is not a function
-
实例8
var tmp = new Date();
function f(){
console.log(tmp);
if(false){
var tmp = 'hello world';
}
}
f();
答案:undefined。
ES6之前只有函数作用域和全局作用域这两个概念。所以在函数f()中var tmp;会被提升到函数顶部,此时输出的tmp并没有被赋值。
-
实例9(有点难度)
var a=10;
function fn(a,b){
console.log(a);
var a=10;
console.log(a);
function a(){}
console.log(a);
}
fn(15);
答案:function a(){}
10
10
变量或函数提升的顺序可以归结为一下这个规律:
函数形参声明--->函数声明---->变量声明