一梆惯、變量提升和函數(shù)提升
首先回顧一下什么是變量提升。
var a=3;
console.log(a);//3
--------------------------------------
console.log(a);//undefined
var a=3;
--------------------------------------
var a;
console.log(a);//undefined
a=3;
變量聲明提升劫樟,通過(guò)var定義(聲明)的變量痪枫,在定義語(yǔ)句之前就可以訪問(wèn)到。
再回顧一下什么是函數(shù)提升叠艳。
function fn(){
console.log("這里是fn函數(shù)");
}
fn();//這里是fn函數(shù)
---------------------------------------
fn();//這里是fn函數(shù)
function fn(){
console.log("這里是fn函數(shù)");
}
---------------------------------------
function fn(){
console.log("這里是fn函數(shù)");
}
fn();//這里是fn函數(shù)
函數(shù)聲明提升奶陈,通過(guò)function聲明的函數(shù),在聲明之前就可以直接調(diào)用(只有函數(shù)聲明的形式才存在函數(shù)提升)附较。
var fn2=function(){
console.log("這里是fn2函數(shù)");
}
fn2();//這里是fn2函數(shù)
---------------------------------------
fn2();//fn2 is not a function
var fn2=function(){
console.log("這里是fn2函數(shù)");
}
---------------------------------------
var fn2;
fn2();//fn2 is not a function
fn2=function(){
console.log("這里是fn2函數(shù)");
}
函數(shù)提升優(yōu)先級(jí)高于變量提升吃粒,且不會(huì)被同名變量聲明時(shí)覆蓋,但是會(huì)被變量賦值后覆蓋
function a(){
console.log("這里是a函數(shù)");
}
var a;
typeof(a)//"function"
---------------------------------------
function a(){
console.log("這里是a函數(shù)");
}
var a=3;
typeof(a)//"number"
二拒课、執(zhí)行上下文
首先根據(jù)代碼的位置徐勃,代碼可以分為全局代碼和函數(shù)(局部)代碼事示。
全局執(zhí)行上下文
console.log(a1,window.a1,this.a1);
var a1=3;
console.log(this);
fn();
function fn(){
console.log("xx");
}
(1)在執(zhí)行全局代碼前將window確定為全局執(zhí)行上下文
(2)對(duì)全局?jǐn)?shù)據(jù)進(jìn)行預(yù)處理
? a.var定義的全局變量==>undefined,添加為window的屬性
? b.function聲明的全局函數(shù)==>創(chuàng)建好函數(shù)對(duì)象,添加為window的方法
? c.this==>賦值(window)
(3)開始執(zhí)行全局的代碼
函數(shù)執(zhí)行上下文
function fn(a1){
console.log(a1);//2
console.log(a2);//undefined
a3();// a3()
console.log(this);//window
var a2=3;
function a3(){
console.log("a3()");
}
}
fn(2)
//fn(2)
(1)在調(diào)用函數(shù)僻肖,準(zhǔn)備執(zhí)行函數(shù)體之前肖爵,創(chuàng)建對(duì)應(yīng)的函數(shù)執(zhí)行上下文對(duì)象(虛擬的)
(2)對(duì)局部數(shù)據(jù)進(jìn)行預(yù)處理
? a.形參變量==>賦值(實(shí)參)==>添加為執(zhí)行上下文的屬性
? b.var定義的局部變量==>undefined,添加為執(zhí)行上下文的屬性
? c.function聲明的函數(shù)==>創(chuàng)建好函數(shù)對(duì)象,添加為執(zhí)行上下文的方法
? d.arguments==>賦值(實(shí)參列表),添加為執(zhí)行上下文的屬性
? e.this==>賦值(調(diào)用函數(shù)的對(duì)象)
(3)開始執(zhí)行函數(shù)體代碼
小結(jié):變量提升就是因?yàn)榇嬖陬A(yù)處理這個(gè)操作臀脏。
三劝堪、執(zhí)行上下文棧
(1)在全局代碼執(zhí)行前,JS引擎就會(huì)創(chuàng)建一個(gè)棧來(lái)存儲(chǔ)管理所有的執(zhí)行上下文對(duì)象
(2)在全局執(zhí)行上下文(window)確定后谁榜,將其添加到棧中(入棧)
(3)在函數(shù)執(zhí)行上下文創(chuàng)建后幅聘,將其添加到棧中(入棧)
(4)在當(dāng)前函數(shù)執(zhí)行完后,將棧頂?shù)膶?duì)象移除(出棧)
(5)當(dāng)所有的代碼執(zhí)行完后窃植,棧中只剩下window
tip:函數(shù)執(zhí)行中,遇到return直接終止可執(zhí)行的代碼,會(huì)直接將當(dāng)前上下文彈出棧
一個(gè)小栗子:
console.log("gb:"+i);
var i=1;
foo(1);
function foo(i){
if(i==4){
return
}
console.log("fb:"+i);
foo(i+1);//遞歸調(diào)用
console.log("fe:"+i);
}
console.log("ge:"+i);
//依次輸出什么帝蒿?
//整個(gè)過(guò)程中產(chǎn)生了幾個(gè)執(zhí)行上下文?
gb:undefiend
fb:1
fb:2
fb:3
fe:3
fe:2
fe:1
be:1
四巷怜、小結(jié)
(1)我們把JS引擎處理JS代碼的處理機(jī)制(預(yù)處理)產(chǎn)生的現(xiàn)象叫做函數(shù)提升和變量提升葛超。
(2)執(zhí)行上下文的數(shù)量為N+1(1指的是全局指向上下文,N指的是函數(shù)調(diào)用的次數(shù))