變量提升
先說三句總結性的話:
-
let 的「創建」過程被提升了,但是初始化沒有提升。
-
var 的「創建」和「初始化」都被提升了。
-
function 的「創建」「初始化」和「賦值」都被提升了。
所以,我們要注意,這三種變量提升,含義是不同的。
變量提升的規律
在進入一個執行上下文后,先把 var 和 function 聲明的變量前置,再去順序執行代碼。
PS:作用域分為全局作用域和函數作用域,用var聲明的變量,只在自己所在的所用域有效。
我們舉例來看看下面的代碼。
代碼 1:
console.log(fn);
var fn = 1;
function fn() {
}
console.log(fn);
相當於:
var fn = undefined;
function fn() {
}
console.log(fn);
fn = 1;
console.log(fn);
打印結果:
代碼 2:
console.log(i);
for (var i = 0; i < 3; i++) {
console.log(i)
}
相當於:
var i = undefined;
console.log(i);
for (i = 0; i < 3; i++) {
console.log(i);
}
打印結果:
代碼 3:
var a = 1;
function fn() {
a = 2;
console.log(a)
var a = 3;
console.log(a)
}
fn();
console.log(a);
相當於:
var a = undefined;
function fn() {
var a
a = 2
console.log(a)
a = 3
console.log(a)
};
a = 1;
fn();
console.log(a);
打印結果:
聲明時的重名問題
假設a
被聲明為變量,緊接着a
又被聲明為函數,原則是:聲明會被覆蓋(先來后到,就近原則)。
PS:
-
如果
a
已經有值,再用 var 聲明是無效的。 -
如果
a
已經有值,緊接着又被賦值,則賦值會被覆蓋。
舉例1:
var fn; //fn被聲明為變量
function fn() {// fn被聲明為function,就近原則
}
console.log(fn); //打印結果:function fn(){}
舉例2:
function fn() {} //fn被聲明為function,且此時fn已經被賦值,這個值就是function的對象
var fn; //fn已經在上一行被聲明且已經有值, 再 var 無效,並不會重置為 undefined
console.log(fn) //打印結果:function fn(){}
既然再var無效,但是再function,是有效的:
function fn() {} //fn被聲明為function,且此時fn已經有值,這個值就是function的對象
function fn() { //此時fn被重復賦值,會覆蓋上一行的值
console.log('smyhvae');
}
console.log(fn)
打印結果:
函數作用域中的變量提升(兩點提醒)
提醒1:
在函數作用域也有聲明提前的特性:
-
使用var關鍵字聲明的變量,是在函數作用域內有效,而且會在函數中所有的代碼執行之前被聲明
-
函數聲明也會在函數中所有的代碼執行之前執行
因此,在函數中,沒有var聲明的變量都會成為全局變量,而且並不會提前聲明。
舉例1:
var a = 1;
function foo() {
console.log(a);
a = 2; // 此處的a相當於window.a
}
foo();
console.log(a); //打印結果是2
上方代碼中,foo()的打印結果是1
。如果去掉第一行代碼,打印結果是Uncaught ReferenceError: a is not defined
提醒2:定義形參就相當於在函數作用域中聲明了變量。
function fun6(e) {
console.log(e);
}
fun6(); //打印結果為 undefined
fun6(123);//打印結果為123
其他題目
var a = 1;
if (a > 0) {
console.log(a);
var a = 2;
}
console.log(a);
打印結果:
1
2
上方代碼中,不存在塊級作用域的概念。if語句中用var定義的變量,仍然是全局變量。
順便延伸一下,用let定義的變量,是在塊級作用域內有效。