
看到這里,我相信你已經(jīng)知道了作用域的概念了,以及根據(jù)聲明的位置和方式將變量分配給作用域的相關(guān)原理了。函數(shù)作用域和塊作用域的行為是一樣的,可以總結(jié)為:任何聲明在某個(gè)作用域內(nèi)的變量,都將附屬于這個(gè)作用域。
首先我們上個(gè)例子
console.log(a);
console.log(foo);
var a=1;
function foo(){
console.log(2);
}
如果你還不知道答案或者不確定,那我相信下面的內(nèi)容一定會(huì)讓你有收獲。首先我們來(lái)回顧一下作用域的一些知識(shí),引擎會(huì)在解釋JavaScript 代碼之前首先對(duì)其進(jìn)行編譯。編譯階段中的一部分工作就是找到所有的聲明,并用合適的作用域?qū)⑺鼈冴P(guān)聯(lián)起來(lái)。也就是詞法作用域的核心,所以包含所有聲明的變量或者聲明的函數(shù)在執(zhí)行之前都會(huì)先被處理。
var a=1;
比如聲明一個(gè)變量并賦值為1,表面上看只是一個(gè)聲明,實(shí)際上。js會(huì)看處理成兩個(gè)聲明,var a和a=1,第一個(gè)定義聲明是在編譯階段進(jìn)行的。第二個(gè)賦值聲明會(huì)被留在原地等待執(zhí)行階段。
var a //編譯階段
a=1 //代碼執(zhí)行階段
也就是變量和函數(shù)感覺(jué)在執(zhí)行之前已經(jīng)被處理過(guò)了,這個(gè)過(guò)程就好像變量和函數(shù)聲明從它們?cè)诖a中出現(xiàn)的位置被“移動(dòng)”到了最上面。這個(gè)過(guò)程就叫作提升。但是要牢牢記?。褐挥斜宦暶鞯暮瘮?shù)或者變量才會(huì)被提升,也就是只有聲明本身會(huì)被提升,而賦值或其他運(yùn)行邏輯會(huì)留在原地等待代碼執(zhí)行。那有個(gè)疑問(wèn)?就是變量提升了,為什么值回事undefined,而函數(shù)提升了,確實(shí)整個(gè)函數(shù)都有值。
foo() //1
function foo(){
console.log(1);
}
console.log(a); //undefined
var a=1;
實(shí)際上,函數(shù)聲明和變量聲明都會(huì)被提升。但是一個(gè)值得注意的細(xì)節(jié)(這個(gè)細(xì)節(jié)可以出現(xiàn)在有多個(gè)“重復(fù)”聲明的代碼中)是函數(shù)會(huì)首先被提升,然后才是變量。這里引進(jìn)一個(gè)新的概念,執(zhí)行上下文:每次當(dāng)引擎執(zhí)行到可執(zhí)行代碼的時(shí)候,就會(huì)進(jìn)入一個(gè)創(chuàng)建一個(gè)執(zhí)行上下文。我們可以理解為,執(zhí)行上下文就是代碼的執(zhí)行環(huán)境,會(huì)形成一個(gè)作用域。
首先在這個(gè)執(zhí)行上下文的創(chuàng)建階段和代碼執(zhí)行階段
創(chuàng)建階段,會(huì)創(chuàng)建變量對(duì)象,建立作用域鏈,以及確定this的指向。
首先我們需要理解下變量對(duì)象(Variable Object)VO,可以這樣理解,變量對(duì)象就是相當(dāng)于一個(gè)容器,它會(huì)做幾件事情
1).創(chuàng)建arguments對(duì)象。搜索當(dāng)前上下文中的參數(shù),確定該對(duì)象下的屬性與屬性值。
2).檢查當(dāng)前上下文的function關(guān)鍵字聲明的函數(shù),找到之后給當(dāng)前那個(gè)函數(shù)賦予一個(gè)引用,該引用指向該函數(shù)所在內(nèi)存地址的引用,如果函數(shù)名的屬性已經(jīng)存在過(guò),那么該屬性將會(huì)被最新新的引用所覆蓋。之后繼續(xù)檢查當(dāng)前上下文中的var 變量聲明,每找到一個(gè)變量聲明,就在變量對(duì)象中給變量一個(gè)屬性,屬性值為undefined。如果存在同名屬性,為了防止同名的函數(shù)被修改為undefined,則會(huì)直接跳過(guò),原屬性值不會(huì)被修改。
這種形式也就是為什么變量聲明和函數(shù)聲明中,函數(shù)聲明會(huì)比變量聲明要提前,也就是函數(shù)優(yōu)先。
代碼執(zhí)行階段:變量對(duì)象會(huì)變成活動(dòng)對(duì)象(Active Object)AO,此時(shí)會(huì)做幾件事情:變量會(huì)進(jìn)行賦值,函數(shù)引用,執(zhí)行其他代碼
當(dāng)然,這里要注意函數(shù)參數(shù)的提升,實(shí)際上函數(shù)參數(shù)是一個(gè)隱式的提升和賦值操作。
function foo (a){
//函數(shù)參數(shù)進(jìn)行隱式賦值 var a ; a=2;
console.log(a);
}
foo(2);
下面在看下代碼就知道為什么會(huì)那樣執(zhí)行了。
foo(); //在變量對(duì)象創(chuàng)建階段,也就是代碼執(zhí)行之前,foo已經(jīng)有了自己在內(nèi)存空間的函數(shù)引用了,所有函數(shù)會(huì)執(zhí)行 輸出 1
function foo(){
console.log(1);
}
console.log(a); //在變量對(duì)象創(chuàng)建階段,就是代碼執(zhí)行之前,變量對(duì)象找到var a;并且賦予a一個(gè)屬性值,該值為undefined;所有輸出 undefined
var a=1;
console.log(a) //代碼執(zhí)行階段,完成變量賦值,所以會(huì)輸出 1.
foo(); // 3 //盡管重復(fù)的var 聲明會(huì)被忽略掉,但出現(xiàn)在后面的函數(shù)聲明還是可以覆蓋前面的。
function foo() {
console.log( 1 );
}
var foo = function() {
console.log( 2 );
};
function foo() {
console.log( 3 );
}
當(dāng)然啦,我們只是討論提升的知識(shí),至于this之類的,后面我們?cè)賮?lái)討論。 留下幾道練習(xí)給可以試一試看掌握的程度怎么樣
console.log(a);
var a=1;
console.log(a);
function a(){
console.log(2)
}
console.log(a);
function foo(a){
console.log(a);
var a=1;
console.log(a);
}
foo(2);
歡迎訪問(wèn)我的個(gè)人網(wǎng)站zhengyepan
學(xué)習(xí)筆記,時(shí)間匆忙,歡迎交流,還有很多遺漏的知識(shí)點(diǎn),以后會(huì)去深入交流~