JavaScript之理解作用域

在javascript的學(xué)習(xí)中,執(zhí)行環(huán)境、作用域是2個(gè)非常非常重要和基本的概念,理解了這2個(gè)概念對(duì)于javsacript中很多腳本的運(yùn)行結(jié)果就能明白其中的道理了,比如搞清作用域和執(zhí)行環(huán)境對(duì)于閉包的理解至關(guān)重要。

一、執(zhí)行環(huán)境(exection context,也有稱之為執(zhí)行上下文)

執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了它們各自的行為。每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對(duì)象 (variable object),環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對(duì)象中。雖然我們編寫的代碼無法訪問這個(gè)對(duì)象,但解析器會(huì)處理數(shù)據(jù)時(shí)會(huì)在后臺(tái)使用它。 ---《JavaScript高程》

當(dāng)這個(gè)執(zhí)行環(huán)境是函數(shù)的時(shí)候,這個(gè)變量對(duì)象就是函數(shù)的活動(dòng)對(duì)象?;顒?dòng)對(duì)象最開始時(shí)只包含一個(gè)變量,即 arguments 對(duì)象(這個(gè)對(duì)象在全局環(huán)境中是不存在的)。

二、作用域鏈(scope chain)

了解了變量對(duì)象之后,理解作用域鏈就容易了。

在代碼在一個(gè)環(huán)境執(zhí)行時(shí),會(huì)創(chuàng)建變量對(duì)象的一個(gè) 作用域鏈。作用域鏈的用途,是保證對(duì)執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。

整個(gè)作用域鏈?zhǔn)怯刹煌瑘?zhí)行位置上的變量對(duì)象(Variable Object)按照規(guī)則所構(gòu)建一個(gè)鏈表。

作用域鏈的前端,始終都是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對(duì)象。下一個(gè)變量對(duì)象來自包含(外部)環(huán)境,而下一個(gè)變量對(duì)象則來自下一個(gè)包含對(duì)象。這樣,一直延續(xù)到全局執(zhí)行環(huán)境;全局執(zhí)行環(huán)境的變量對(duì)象始終都是作用域鏈的最后一個(gè)對(duì)象。

引用高程中的例子:

 var color="blue";
 function changecolor(){
    var anothercolor="red";
    function swapcolors(){
    var tempcolor=anothercolor;
    anothercolor=color;
    color=tempcolor;
       // Todo something        
     }
    swapcolors();
}
changecolor();
 //這里不能訪問tempcolor和anocolor;但是可以訪問color;
alert("Color is now  "+color);

上面例子涉及了3個(gè)執(zhí)行環(huán)境:全局變量、changeColor()的局部變量環(huán)境和swapColors()的局部變量環(huán)境。swapcolor()的局部環(huán)境開始先在自己的Variable Object中搜索變量和函數(shù)名,找不到,則向上搜索changecolor作用域鏈。。。。。以此類推。但是,changecolor()函數(shù)是無法訪問swapcolor中的變量。

作用域鏈

通過上面的分析,我們可以得知內(nèi)部環(huán)境可以通過作用域鏈訪問所有的外部環(huán)境,但外部環(huán)境不能訪問內(nèi)部環(huán)境中的任何變量和函數(shù)。這些環(huán)境之間是線性、有次序的。每個(gè)環(huán)境都可以向上搜索作用域鏈,以便查詢變量和函數(shù)名;

tip:通過下面兩種情況,即當(dāng)執(zhí)行流進(jìn)入下列任何語句時(shí),作用域鏈就會(huì)得到加長:

  • try-catch 語句的 catch 塊;
  • with 語句。

三、沒有塊級(jí)作用域(在 let 出現(xiàn)以前)

看兩個(gè)例子

情況1:
for (var i = 0;i<10;i++) {
  // do something
}
console.info(i);  // 10
情況2:
for (let i = 0;i<10;i++) {
  // do something
}
console.info(i);  // error

從上面的例子中可以看出,JavaScript沒有像其他類C的語言(由花括號(hào)封閉的代碼塊都有自己的作用域)一樣。在使用for語句時(shí)尤其記住這點(diǎn)。

四、聲明變量

使用var聲明變量時(shí),這個(gè)變量將被自動(dòng)添加到距離最近的可用環(huán)境中。對(duì)于函數(shù)而言,自然聲明的變量就會(huì)被添加到函數(shù)的局部環(huán)境中,變量在整個(gè)函數(shù)環(huán)境內(nèi)都是可用的。

但是,如果變量沒有是用var進(jìn)行聲明,將會(huì)被添加到全局環(huán)境,也就是說成位全局變量了。(只有當(dāng)執(zhí)行流執(zhí)行到該語句時(shí),才會(huì)被添加到全局變量的變量對(duì)象中)

看一個(gè)例子:

情況1:
var x = 1;
function rain() {
  console.info(x);  // 1
}
rain(); 
情況2:
var x = 1;
function rain() {
  console.info(x);  //undefined
  var x = 10;
  console.info(x); // 10 
}
rain(); 

情況1中得到的1 和 情況2中得到的10,通過上述作用域鏈的關(guān)系,我們較為容易理解。那情況2中的 undefined 是怎么回事兒呢?答案是預(yù)解析,我們看下面一段代碼,和上面的代碼等價(jià)

var x = 1;
function rain() {
  var x;
  console.info(x);  //undefined
  x = 10;
  console.info(x); // 10 
}
rain(); 

因?yàn)镴avaScript引擎會(huì)預(yù)解析代碼中變量和函數(shù)的定義。導(dǎo)致調(diào)用x的時(shí)候,局部執(zhí)行環(huán)境已經(jīng)有了x,就不向上繼續(xù)尋找x,也就訪問不到全局變量的x。而此時(shí)局部變量x還未賦值,這時(shí)候輸出也就是 undefined 了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容