Js代碼分為兩個(gè)階段:編譯階段和執(zhí)行階段
我們習(xí)慣將var a = 2;看作一個(gè)聲明喻犁,而實(shí)際上JavaScript引擎并不這么認(rèn)為爽篷。它將var a和a = 2當(dāng)作兩個(gè)單獨(dú)的聲明屡萤,第一個(gè)是編譯階段的任務(wù)漫仆,而第二個(gè)則是執(zhí)行階段的任務(wù)颇蜡。
這意味著無論作用域中的聲明出現(xiàn)在什么地方漾脂,都將在代碼本身被執(zhí)行前首先進(jìn)行處理假颇。可以將這個(gè)過程形象地想象成所有的聲明(變量和函數(shù))都會(huì)被“移動(dòng)”到各自作用域的最頂端骨稿,這個(gè)過程被稱為 提升笨鸡。
聲明本身會(huì)被提升姜钳,而包括函數(shù)表達(dá)式的賦值在內(nèi)的賦值操作并不會(huì)提升。
1. 變量聲明提升
Js編譯器會(huì)把變量聲明看成兩個(gè)部分: 聲明操作(var a) 和 賦值操作(a=2)
聲明操作在編譯階段進(jìn)行形耗,聲明操作會(huì)被提升到執(zhí)行環(huán)境的頂部哥桥,值是undefined(表示未初始化),賦值操作會(huì)被留在原地等待執(zhí)行階段
- 來一個(gè)例子:
console.log(name); //undefined
var name = 'aman';
//相當(dāng)于
var name;
console.log(name); //undefined
name = 'aman';
- 再來一個(gè)例子:
var name = 'aman1';
function getName(){
console.log(name ); //undefined
var name = 'aman2';
console.log(name ); //aman2
}
getName();
//相當(dāng)于
var name = 'aman1';
function getName(){
var name;
console.log(name ); //undefined
name = 'aman2';
console.log(name ); //aman2
}
getName();
2. 函數(shù)聲明提升
定義函數(shù)有兩種方式:
- 函數(shù)聲明
- 函數(shù)表達(dá)式
1. 函數(shù)聲明: 函數(shù)聲明提升會(huì)在編譯階段把聲明和函數(shù)體整體都提前到執(zhí)行環(huán)境頂部激涤,所以我們可以在函數(shù)聲明之前調(diào)用這個(gè)函數(shù)
foo(); //foo
function foo(){ console.log('foo') }
2. 函數(shù)表達(dá)式:其實(shí)就是變量聲明的一種拟糕,聲明操作會(huì)被提升到執(zhí)行環(huán)境頂部,并賦值undefined倦踢。賦值操作被留在原地等到執(zhí)行送滞。
foo(); //報(bào)錯(cuò):TypeError: foo is not a function
var foo = function(){ console.log('foo') }
//相當(dāng)于
var foo;
foo();
foo = function(){ console.log('foo') }
3. 控制語句
Js中使用函數(shù)級作用域,不存在塊級作用域辱挥。所有普通塊中的聲明都會(huì)被提升到頂部犁嗅,所以控制語句對聲明的控制就顯得完全沒有效果
if(false){
var name = 'aman';
}
console.log(name); //undefined
//相當(dāng)于
var name;
if(false){
name = 'aman';
}
console.log(name); //undefined
4. 函數(shù)優(yōu)先
提升操作會(huì)優(yōu)先進(jìn)行函數(shù)的聲明
函數(shù)會(huì)首先被提升然后才是變量晤碘,重復(fù)的變量聲明會(huì)被忽略褂微,只剩下賦值操作,多個(gè)函數(shù)聲明可以進(jìn)行
聲明的順序是這樣的:
- 找到所有的函數(shù)聲明园爷,初始化函數(shù)體宠蚂,如有同名的函數(shù)則會(huì)進(jìn)行覆蓋
- 查找變量聲明,初始化為undefined童社,如果已經(jīng)存在同名的變量求厕,就什么也不做直接略過
foo(); //b
function foo() { console.log("a"); }
function foo() { console.log("b"); }
var foo = 100;
console.log(foo); //100
//相當(dāng)于
//編譯階段:
function foo() { console.log("a"); }
function foo() { console.log("b"); } //覆蓋上邊的聲明
// var foo; //忽略
//執(zhí)行階段:
foo(); //b
foo = 100;
console.log(foo); //100
注意: 盡量避免在控制語句內(nèi)使用函數(shù)表達(dá)式聲明函數(shù),如下
一個(gè)特別的例子
console.log(foo); // (早期的瀏覽器會(huì)輸出 b叠洗,現(xiàn)在是 undefined)
var a = true;
if (a) {
function foo() { console.log("a"); }
} else {
function foo() { console.log("b"); }
}