变量提升

JavaScript作为解释型语言,与编译型语言有很大的不同,变量的声明会更加灵活,但同时也会带来一些问题,变量提升就是其中的一个需要特别注意的地方。

变量提升(对于变量)

1
2
3
4
5
6
7
8
9
10
11
12
13
var myvar = '变量值'; 
console.log(myvar); // 变量值
//代码段2--------------------------
var myvar = '变量值2';
(function() {
console.log(myvar); //变量值2
})();
//代码段3----------------------------
var myvar = '变量值';
(function() {
console.log(myvar); // undefined
var myvar = '内部变量值';
})();

代码段1会在控制台打印出 变量值 ,很容易理解;代码段2也会在控制台打印出 变量值 ,Javascript编译器首先在匿名函数内部作用域(Scope)查看变量myvar是否声明,发现没有,就继续向上一级的作用域(Scope)查看是否声明 myvar,发现存在,即打印出该作用域的myvar值。但对于代码3,变量声明进行了提升,等同于下面的代码:

1
2
3
4
5
6
var myvar = '变量值'; 
(function() {
var myvar ; //undefined;
console.log(myvar); // undefined
myvar = '内部变量值';
})();

变量提升(对于函数)

Javascript Function有两种类型:
1、

1
2
3
4
// 函数声明
function funDeclaration(type){
return type==="Declaration";
}

2、

1
2
3
4
// 函数表达式
var funExpression = function(type){
return type==="Expression";
}

然后运行下面两段代码:

1
2
3
4
5
6
7
8
9
funDeclaration("Declaration");//=> true
function funDeclaration(type){
return type==="Declaration";
}

funExpression("Expression");//=>error
var funExpression = function(type){
return type==="Expression";
}

这边也涉及到变量提升,上面的代码等同于:

1
2
3
4
5
6
7
8
9
10
function funDeclaration(type){
return type==="Declaration";
}
funDeclaration("Declaration");//=> true

var funExpression;
funExpression("Expression");//==>error
funExpression = function(type){
return type==="Expression";
}

再来看看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var sayHello;
console.log(typeof (sayHey));//=>function
console.log(typeof (sayHello));//=>undefined
if (true) {
function sayHey() {
console.log("sayHey");
}
sayHello = function sayHello() {
console.log("sayHello");
}
} else {
function sayHey() {
console.log("sayHey2");
}
sayHello = function sayHello() {
console.log("sayHello2");
}
}
sayHey();// => sayHey2
sayHello();// => sayHello

sayHey是用函数声明创建的,在JS解析时JS编译器将函数定义进行了函数提升,也就是说,在解析JS代码的时候,JS编译器(条件判断不形成新的作用域,两个sayHey函数定义都被提升到条件判断之外)检测到作用域内有两个同名的sayHey定义,第一个定义先被提升,第二个定义接着被提升(第二个定义在第一个定义之下),第二个定义覆盖了第一个sayHey定义,所以sayHey()输出sayHey2;而sayHello是用函数表达式创建的,其表达式的内容是在JS运行时(不是解析时)才能确定(这里条件判断就起到作用了),所以sayHello表达式执行了第一个函数定义并赋值,则sayHello()输出sayHello。
其实代码等同于:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var sayHello;
function sayHey() {
console.log("sayHey");
}
function sayHey() {
console.log("sayHey2");
}
console.log(typeof (sayHey));//=>function
console.log(typeof (sayHello));//=>undefined
if (true) {
//hoisting...
sayHello = function sayHello() {
console.log("sayHello");
}
} else {
//hoisting...
sayHello = function sayHello() {
console.log("sayHello2");
}
}
sayHey();// => sayHey2
sayHello();// => sayHello

所以函数也好,变量也好,如果是编译时声明,跟定义的位置没有关系,编译器会从头往下全文检索一遍,然后用最后面的那个进行定义,但运行时声明就不同,它选择的是定义位置之上检索,如果有,就赋值,没有,就undefined,无论你下文有没有再定义。