提升
JavaScript 在執(zhí)行之前,會先進行編譯。如下代碼:
fn();
console.log(i);
var i = 1;
function fn() {
console.log(i);
}
當執(zhí)行到這段代碼時,會進行兩次聲明。定義聲明時在編譯階段進行,賦值聲明會留在原地等待執(zhí)行階段。
實際代碼執(zhí)行順序:
function fn() {
console.log(i);
}
var i;
fn(); // undefined
console.log(i); // undefined
i = 1;
4.1 變量的提升
通過 var 關鍵字聲明的變量會存在提升,ES6 新增的 let const 關鍵字聲明的變量不會存在提升。
var a = 2;
console.log(a);
var a; // 編譯
a = 2; // 執(zhí)行
console.log(a); // 2(預想的 a 是 undefined 或者直接報錯,但是由于變量聲明的提升,聲明會先進行)
4.2 函數(shù)表達式的提升
通過函數(shù)表達式聲明的函數(shù)與變量的聲明提升是一樣的,但是函數(shù)表達式的聲明提升,不會對函數(shù)進行引用(即該變量并沒有進行賦值),
如下代碼:
console.log(fn(1)); // TypeError, fn is not a function
var fn = function (a) {
return a;
};
fn 存在聲明的提升,但是在調用時 fn 未被賦值,此時的值是 undefined 所以如果被當作函數(shù)進行調用,就會報錯。
上面的代碼相當于:
var fn;
console.log(fn(1));
fn = function (a) {
return a;
};
此處需要注意的是,對于具名的函數(shù)表達式,名稱標識符在賦值之前也無法在所在作用域中使用,如下:
foo(); // TypeError
bar(); // ReferenceError
var foo = function bar() {
// ...
};
4.3 函數(shù)聲明的提升
使用關鍵字 function 聲明的函數(shù)也存在提升,不過函數(shù)聲明被置于執(zhí)行環(huán)境頂部,即使調用函數(shù)的代碼在聲明函數(shù)之前也可以正確訪問。
foo(); // 可以在函數(shù)聲明前進行調用
function foo() {
console.log(2); // 2
}
4.4 提升的優(yōu)先級
函數(shù)聲明 > 函數(shù)表達式 = 變量
函數(shù)聲明和變量聲明都會被提升,但是函數(shù)聲明的優(yōu)先級最高。這里的優(yōu)先級是指進行提升時函數(shù)會首先被提升,然后才是變量。
如果函數(shù)聲明與變量聲明的名稱一致,那么變量的聲明會被當作重復的聲明被忽略。
foo(); // 1
var foo;
function foo() {
console.log(1);
}
foo = function () {
console.log(2);
};
對于兩個相同的函數(shù)聲明,后面的函數(shù)聲明仍然會覆蓋之前的函數(shù)聲明:
foo(); // 3
function foo() {
console.log(1);
}
var foo = function () {
console.log(2);
};
function foo() {
console.log(3);
}
函數(shù)的聲明不會被條件判斷所控制,如下代碼不管條件判斷是否成立,函數(shù)聲明都會被提升(ES6 后引入了塊級作用域,所以此處可能會報錯:TypeError: foo is not a function):
foo(); // "b"
var a = true;
if (a) {
function foo() {
console.log("a");
}
} else {
function foo() {
console.log("b");
}
}