JS是一種腳本語(yǔ)言,和一些高級(jí)語(yǔ)言不同,它沒(méi)有完整編譯的過(guò)程荤牍,一般是邊寫邊編譯,這也是我們覺(jué)得腳本語(yǔ)言比其他語(yǔ)言加載快的原因庆冕,JS中有變量聲明提升這一機(jī)制康吵。
當(dāng)JS在執(zhí)行的時(shí)候會(huì)分為2個(gè)階段,預(yù)解析访递,執(zhí)行晦嵌,當(dāng)JS在執(zhí)行的時(shí)候會(huì)將所有用var聲明的變量以及關(guān)鍵字定義的函數(shù)進(jìn)行提升( function fn(){......} ),提升到當(dāng)前作用域的最頂端拷姿,而賦值語(yǔ)句在原地等待執(zhí)行惭载,預(yù)解析后,再?gòu)纳贤轮鹦薪馕龃a响巢。預(yù)解析遵循一些原則描滔,下面一點(diǎn)一點(diǎn)的說(shuō)明。
提到變量聲明提升踪古,就得結(jié)合作用域來(lái)分析含长,也就是局部作用域能訪問(wèn)到全局的變量券腔,而全局作用域訪問(wèn)不到局部作用域。
1. 函數(shù)聲明提升
函數(shù)的聲明方式
函數(shù)聲明有兩種方式:函數(shù)聲明拘泞,函數(shù)表達(dá)式(又稱函數(shù)字面量聲明)
- 函數(shù)聲明提升會(huì)在編譯階段把聲明和函數(shù)體整體都提前到執(zhí)行環(huán)境頂部纷纫,所以我們可以在函數(shù)聲明之前調(diào)用這個(gè)函數(shù)
- 函數(shù)表達(dá)式,其實(shí)就是變量聲明的一種田弥,聲明關(guān)鍵字會(huì)被提升到執(zhí)行環(huán)境頂部涛酗,并賦值undefined。賦值語(yǔ)句被留在原地等到執(zhí)行偷厦。
①函數(shù)聲明的提升
// 函數(shù)聲明
func()
function func () {
console.log(1);
}
上例不會(huì)報(bào)錯(cuò)商叹,因?yàn)?‘函數(shù)聲明提升’,即將函數(shù)聲明提升(整體)到作用域頂部只泼,實(shí)際提升后結(jié)果同下:
// 函數(shù)聲明提升后
function func () {
}
func() // 1
②函數(shù)表達(dá)式的提升
// 函數(shù)表達(dá)式
baz();
var baz = function(){
console.log(1);
}
上例會(huì)報(bào)錯(cuò)輸出TypeError: baz is not a function
函數(shù)表達(dá)式聲明提升后剖笙,只會(huì)把聲明操作var baz提升到頭部,其他的賦值部分函數(shù)體還留在原地请唱,實(shí)際提升后結(jié)果同下:
//函數(shù)表達(dá)式的提升后
var baz;
baz(); // TypeError: baz is not a function
baz = function() {
console.log(1);
};
2. 變量聲明提升
變量聲明提升(只有var聲明的變量才有變量提升弥咪,let、const無(wú)十绑;變量賦值無(wú)提升)
變量聲明提升會(huì)把var聲明的頭部提升聚至,賦值部分留在原地
1.簡(jiǎn)單的變量聲明提升
console.log(num)
var num = 10
上例輸出undefined,變量聲明提升會(huì)把var num提升本橙,賦值部分num = 10留在原地
// 變量聲明提升后
var num
console.log(num)
num = 10
2.函數(shù)內(nèi)的變量聲明提升
①舉例1
var a=1;
function fn(){
console.log(a);
a=2;
}
fn();
console.log(a);
結(jié)果先輸出1扳躬,后輸出2
解析:當(dāng)fn執(zhí)行后,會(huì)輸出1甚亭,因?yàn)榫植孔饔糜蚰茉L問(wèn)到全局作用域贷币,代碼逐行執(zhí)行,向上尋找亏狰,找到全局變量a = 1役纹,當(dāng)再次console.log(a)時(shí),a = 2暇唾,修改里全局變量a促脉,所以a = 2(如果函數(shù)內(nèi)部不用var聲明a,直接a=2策州,此時(shí)a相當(dāng)于全局變量)
②舉例2
var a = 1;
function fn(){
console.log(a);
var a=2;
}
fn();
console.log(a);
結(jié)果先輸出undefined瘸味,后輸出1
解析:當(dāng)fn執(zhí)行后,會(huì)把函數(shù)內(nèi)部的var聲明的變量提升抽活,所以輸出undefined,console.log(a)取全局變量a = 1
// 變量提升后
var a = 1;
function fn(){
var a
console.log(a);
a=2;
}
fn(); // undefined
console.log(a); // 1
③舉例3
var a=1;
function fn(a){
console.log(a);
a=2;
}
fn(); // undefined
console.log(a); // 1
結(jié)果輸出又不一樣 锰什,此時(shí)執(zhí)行fn函數(shù)下硕,函數(shù)傳一個(gè)參數(shù)丁逝,就相當(dāng)于在函數(shù)內(nèi)聲明了一個(gè)變量,由于fn()沒(méi)有傳參梭姓,所以輸出undefined霜幼,注意此時(shí)函數(shù)內(nèi)部的a =2是函數(shù)內(nèi)部變量也就是參數(shù)a,不是全局變量誉尖。console.log(a)罪既,輸出全局變量 1
別暈,再看一個(gè)例題
④舉例4
var a=1;
function fn(a){
alert(a);
a=2;
}
fn(a); // 1
alert(a); // 1
結(jié)果又是什么呢铡恕?
函數(shù)輸出1琢感,因?yàn)楹瘮?shù)調(diào)用的時(shí)候把全局的a傳了進(jìn)去,即fn(1)探熔,所以函數(shù)內(nèi)部a = 1驹针,,全局仍然輸出1
3.函數(shù)聲明與變量聲明先后順序
函數(shù)聲明會(huì)優(yōu)于變量的聲明诀艰,也就是說(shuō)函數(shù)會(huì)提升到變量的前面
var c = 1;
function d() {
console.log(c);
var c = 2;
}
d();
結(jié)果輸出 undefined 柬甥,函數(shù)整體提升到全局變量var c = 1前面,函數(shù)內(nèi)部的var c提升其垄,結(jié)果如下
// 提升后
function d() {
var c
console.log(c);
c = 2;
}
var c = 1;
d(); // undefined
4.課后習(xí)題
console.log(a);
var a=1;
console.log(a);
function a(){console.log(2)}
console.log(a);
var a=3;
console.log(a)
function a(){console.log(4)}
console.log(a)
a();
第一看到這些代碼誰(shuí)都頭大苛蒲,不過(guò)我們來(lái)一點(diǎn)一點(diǎn)的看。
從上往下看绿满,遇到var a = 1臂外,提升var a ,a = 1留在原地
var a
console.log(a);
a=1;
console.log(a);
function a(){console.log(2)}
console.log(a);
var a=3;
console.log(a)
function a(){console.log(4)}
console.log(a)
a();
繼續(xù)往下棒口,遇到函數(shù)function a(){console.log(2)}寄月,整體提升,把var a替代
function a(){console.log(2)}
console.log(a);
a=1;
console.log(a);
console.log(a);
var a=3;
console.log(a)
function a(){console.log(4)}
console.log(a)
a();
繼續(xù)往下无牵,遇到函數(shù)var a=3漾肮,當(dāng)遇到重名的變量只會(huì)留一個(gè),遇到函數(shù)茎毁,函數(shù)的優(yōu)先級(jí)高克懊,所以留函數(shù),繼續(xù)遇到函數(shù)function a(){console.log(4)}七蜘,提升谭溉,替換function a(){console.log(2)}
function a(){console.log(4)}
console.log(a);
a=1;
console.log(a);
console.log(a);
a=3;
console.log(a)
console.log(a)
a();
最后a(),想上找a ,找到a = 3橡卤,不是函數(shù)報(bào)錯(cuò)
輸出結(jié)果扮念,function a(){console.log(4)} 1 1 3 3 a is not a function
看到這里是不是對(duì)變量和函數(shù)的提升有了進(jìn)一步的了解了呢