2019-11-18 seo達人
JS----預編譯及變量提升詳解
JS屬于解釋型語言,在執(zhí)行過程中順序執(zhí)行,但是會分塊先預編譯然后才執(zhí)行。因此在JS中存在一種變量提升的現象。搞懂預編譯環(huán)節(jié),變量提升自然而然也就懂了。本文講圍繞以下幾點進行介紹(變量提升會穿插在其中講解):
預編譯執(zhí)行步驟
示例演示
預編譯執(zhí)行步驟
預編譯發(fā)生在函數執(zhí)行的前一刻,過程如下:
創(chuàng)建AO對象,執(zhí)行期上下文(后面更新關于執(zhí)行期上下文詳解)。
尋找函數的形參和變量聲明,將變量和形參名作為AO對象的屬性名,值設定為undefined.
將形參和實參相統一,即更改形參后的undefined為具體的形參值。
尋找函數中的函數聲明,將函數名作為AO屬性名,值為函數體。
至此,預編譯環(huán)節(jié)結束,函數中咯變量按照最終AO對象中的值開始執(zhí)行。接下來,結合示例演示就會更加清晰。
作者:北海北方
鏈接:https://juejin.im/post/5aa6693df265da23884cb571
來源:掘金
著作權歸作者所有。商業(yè)轉載請聯系作者獲得授權,非商業(yè)轉載請注明出處。
示例演示
我們先來看下面這段代碼:
function fn(a){
console.log(a);
var a = 123;
console.log(a);
function a(){};
console.log(a);
var b = function(){};
console.log(b);
function d(){};
}
//調用函數
fn(1);
作者:北海北方
鏈接:https://juejin.im/post/5aa6693df265da23884cb571
來源:掘金
著作權歸作者所有。商業(yè)轉載請聯系作者獲得授權,非商業(yè)轉載請注明出處。
接下來我們來按照前面的步驟詳細分析它的預編譯執(zhí)行過程:
創(chuàng)建AO對象
AO{
//空對象
}
復制代碼
找形參和變量聲明
AO{
a : undefined,
b : undefined
}
復制代碼
形參和實參相統一
AO{
a : 1,
b : undefined
}
復制代碼
找函數聲明
AO{
a : function a(){},
b : undefined,
d : function d(){}
}
復制代碼預編譯環(huán)節(jié)就此結束,此時的AO對象已經更新為:
AO{
a : function a(){},
b : undefined,
d : function d(){}
}
復制代碼函數開始逐行順序執(zhí)行:
function fn(a){
console.log(a);// 輸出functiona(){}
var a = 123;//執(zhí)行到這里重新對a賦,AO對象再一次更新
console.log(a);// 輸出123
function a(){};//預編譯環(huán)節(jié)已經進行了變量提升,故執(zhí)行時不在看這行代碼
console.log(a);// 輸出123
var b = function(){};//這個是函數表達式不是函數聲明,故不能提升,會對AO中的b重新賦值
console.log(b);//輸出function(){}
function d(){};
}
復制代碼至此,函數執(zhí)行完畢,銷毀AO對象。
我們再來看幾個例子,熟悉函數的預編譯過程。
示例一:
function test (a,b){
console.log(a);
c = 0;
var c;
a = 3;
b = 2;
console.log(b);
function b(){};
function d(){};
console.log(b);
}
//調用函數
test(1);
復制代碼它的AO創(chuàng)建過程如下(此處省略創(chuàng)建空AO對象的部分,下文同):
AO1{
a : undefined,
b : undefined,
c : undefined
}
AO2{
a : 1,
b : undefined,
c : undefined
}
AO3{
a : 1,
b : function b(){},
c : undefined,
d : function d(){}
}
復制代碼至此預編譯環(huán)節(jié)完成,開始執(zhí)行:
function test (a,b){
console.log(a); //輸出1
c = 0; //給AO對象中的c重新賦值0
var c;//預編譯環(huán)節(jié)變量提升,不再讀此行代碼
a = 3;//給AO對象中的a重新賦值3
b = 2;//給AO對象中的b重新賦值2
console.log(b);//輸出2
function b(){};//預編譯環(huán)節(jié)變量提升,執(zhí)行時不再讀這行代碼
function d(){};//預編譯環(huán)節(jié)變量提升,執(zhí)行時不再讀這行代碼
console.log(b);//輸出2
}
//調用函數
test(1);
復制代碼示例二:
這個例子中我們引入全局對象GO。GO與AO的過程類似
function test(){
var a = b = 123;
}
test();
復制代碼此函數的執(zhí)行過程:先把123賦給b,再聲明a,再把b賦給a。此時變量b未經聲明就賦值,為全局變量。預編譯環(huán)節(jié)如下:
GO1{
b : undefined
}
AO1{
a : undefined
}
GO2{
b : 123;
}
AO2{
a : 123;
}
復制代碼示例三 :
console.log(test);
function test(test){
console.log(test);
var test = 234;
console.log(test);
function test(){};
}
test(1);
var test = 123;
復制代碼我們來看它的預編譯過程:
//執(zhí)行前(頁面加載完成時)生成GO對象
GO1{
test : undefined
}
GO2{
test : function(){}
}
//輸出 function test(){...}
//執(zhí)行test()前生成它的AO對象
AO1{
test : undefined
}
AO2{
test : 1
}
AO3{
test : function test(){}
}
//預編譯結束開始執(zhí)行test(1);
AO4{
test : 234
}
//輸出234
復制代碼示例四:
function demo(){
console.log(b);
if(a){
var b = 100;
}
console.log(b);
c = 234;
console.log(c);
}
var a;
demo();
a = 10;
console.log(c);
復制代碼我們來看它的預編譯過程:
//首先是全局對象GO
GO1{
a : undefined
}
G02{
a : undefined,
demo : function demo(){}
}
//執(zhí)行demo()前預編譯,由于demo中的c未聲明就使用故為全局對象
//輸出undefined
GO3{
a : undefined,
demo : function demo(){}
c : undefined
}
//此時a還是undefined,故不執(zhí)行if()代碼塊
//輸出還是undefined
GO4{
a : undefined,
demo : function demo(){}
c : 234;
}
//輸出234
GO5{
a : 10,
demo : function demo(){}
c : 234;
}
//輸出234