Javascript:對作用域鏈的認識

作用域鏈

作用域:提高了程序邏輯的局部性,增強程序的可靠性,減少名字沖突。
看一個例子:

var s1 = 'sharpxiajun';

function ftn(){
  var s2 = 'xtq';
  
  console.log(this); //window
  console.log('s1:' + this.s1 + ';s2:' + this.s2); // s1:sharpxiajun;s2:undefined
  console.log('s1:'+this.s1+';s2:' + s2); //s1:sharpxiajun;s2:xtq
}
ftn();

在javascript世界里有一個大的作用域環(huán)境,這個環(huán)境就是window,window環(huán)境不需要我們自己使用什么方式構(gòu)建,頁面加載時候頁面會自動構(gòu)造的,上面代碼里有一個大括號,這個大括號是對函數(shù)的定義,運行時,我們發(fā)現(xiàn)函數(shù)作用域內(nèi)部定義的s2變量是不能被window對象訪問的,因此s2變量是被{}保護起來了,它的生命周期和這個函數(shù)的生命周期有關(guān)。

在javascript里作用域有一個專門的定義execution context,叫執(zhí)行上下文或執(zhí)行環(huán)境,我們來想想javascript里那些情況是執(zhí)行:
情況一:當(dāng)頁面加載時候在script標(biāo)簽下的javascript代碼會按順序執(zhí)行,而這些能被執(zhí)行的代碼都是屬于window的變量或函數(shù);
情況二:當(dāng)函數(shù)的名字后面加上小括號(),例如ftn(),這也是在執(zhí)行,不過它執(zhí)行的是函數(shù)。

如此說來,javascript里的執(zhí)行環(huán)境有兩類一類是全局執(zhí)行環(huán)境,即window代表的全局環(huán)境,一類是函數(shù)代表的函數(shù)執(zhí)行環(huán)境,這也就是我們常說的局部作用域。

執(zhí)行環(huán)境在javascript語言里并非是一個抽象的概念,而是有具體的實現(xiàn),這個實現(xiàn)其實是個對象,這個對象也有個名字叫做variable object,稱為上下文變量, 上下文變量存儲的是上下文變量所處執(zhí)行環(huán)境里定義的所有的變量和函數(shù)。

全局執(zhí)行環(huán)境的上下文變量是可以訪問到的,它就是window對象,所以我們說window能代表全局作用域是有道理的,但是局部作用域即函數(shù)的執(zhí)行環(huán)境里的上下文變量是代碼不能訪問到的,不過javascript引擎在處理數(shù)據(jù)時候會使用到它。

在javascript語言里還有一個概念,它的名字叫做execution context stack,就是執(zhí)行環(huán)境棧,每個要被執(zhí)行的函數(shù)都會先把函數(shù)的執(zhí)行環(huán)境壓入到執(zhí)行環(huán)境棧里,函數(shù)執(zhí)行完畢后,這個函數(shù)的執(zhí)行環(huán)境就會被執(zhí)行環(huán)境棧彈出,例如上面的例子:函數(shù)執(zhí)行時候函數(shù)的執(zhí)行環(huán)境會被壓入到執(zhí)行環(huán)境棧里,函數(shù)執(zhí)行完畢,執(zhí)行環(huán)境棧會把這個環(huán)境彈出,執(zhí)行環(huán)境棧的控制權(quán)就會交由全局環(huán)境,如果函數(shù)后面還有代碼,那么代碼就是接著執(zhí)行。如果函數(shù)里嵌套了函數(shù),那么嵌套函數(shù)執(zhí)行完畢后,執(zhí)行環(huán)境棧的控制權(quán)就交由了外部函數(shù),然后依次類推,最后就是全局執(zhí)行環(huán)境了。

講到這里我們大名鼎鼎的作用域鏈要登場了,函數(shù)的執(zhí)行環(huán)境被壓入到執(zhí)行環(huán)境棧里后,函數(shù)就要執(zhí)行了,函數(shù)執(zhí)行的第一步不是執(zhí)行函數(shù)里的第一行代碼而是在上下文變量里構(gòu)造一個作用域鏈,作用域鏈的英文名字叫做scope chain,作用域鏈的作用是保證執(zhí)行環(huán)境里有權(quán)訪問的變量和函數(shù)是有序的,這個概念里有兩個關(guān)鍵意思:有權(quán)訪問和有序,我們看看下面的代碼:

var b1 = 'b1';
function ftn1(){

  var b2 = 'b2';
  var b1 = 'bbb';

  function ftn2(){
    var b3 = 'b3';
    b2 = b1;
    b1 = b3;

    console.log('b1:'+ b1 +';b2:' + b2 +';b3' + b3); 
    // b1:b3; b2:bbb; b3:b3
  }
  
  ftn2();
}

ftn1();
console.log(b1);

這個例子我們發(fā)現(xiàn),ftn2函數(shù)可以訪問變量b1,b2,這個體現(xiàn)了有權(quán)訪問的概念,當(dāng)ftn1作用域里改變了b1的值并且把b1變量重新定義為ftn1的局部變量,那么ftn2訪問到的b1就是ftn1的,ftn2訪問到b1后就不會在全局作用域里查找b1了,這個體現(xiàn)了有序性。

廣大程序員對作用域鏈的理解有兩塊一塊是作用域,而作用域在javascript語言里指的是執(zhí)行環(huán)境execution context,執(zhí)行環(huán)境在javascript引擎里是通過上下文變量體現(xiàn)的variable object,javascript引擎里還有一個概念就是執(zhí)行環(huán)境棧execution context stack,當(dāng)某一個函數(shù)的執(zhí)行環(huán)境壓入到了執(zhí)行環(huán)境棧里,這個時候就會在上下文變量里構(gòu)造一個對象,這個對象就是作用域鏈scope chain,而這個作用域鏈就是廣大程序員理解的第二塊知識,作用域鏈的作用是保證執(zhí)行環(huán)境里有權(quán)訪問的變量和函數(shù)是有序的,作用域鏈的變量只能向上訪問,變量訪問到window對象即被終止,作用域鏈向下訪問變量是不被允許的。

很多人常常認為作用域鏈是理解this指針的關(guān)鍵,這個理解是不正確的,this指針構(gòu)造是和作用域鏈同時發(fā)生的,也就是說在上下文變量構(gòu)造作用域鏈的同時還會構(gòu)造出一個this對象,this對象是屬于上下文變量,而this變量的值就是當(dāng)前執(zhí)行環(huán)境外部的上下文變量的一份拷貝,這份拷貝里沒有作用域鏈變量的

function show () {
  alert(this);
  function show2 () {
    alert(this);
  }
  show2();
}
show();

我們看到函數(shù)show 和 show2里的shis指針都是指向window,這是為什么了?因為在JavaScript我們定義函數(shù)方式是通過function xxx() {}形式,那么這個函數(shù)不管定義在哪里(包括匿名函數(shù)),它都是屬于全局對象window,所以他們的執(zhí)行環(huán)境的外部的執(zhí)行上下文都是指向window。

var a = 10;
function test(){
  a = 100;
  alert(a);
  alert(this.a);
  var a;
  alert(a);
} 
test();
//正確答案:100 10 100
var a = 100;
function test(){
  alert(a);
  var a = 10;
  alert(a);
}
test();
//正確答案: undefined 10
var a = 100;
function test(){
  alert(a);
  a = 10;
  alert(a);
}
test();
alert(a);
//正確答案:100 10 10;

參考文獻:http://www.cnblogs.com/sharpxiajun/p/4133984.html

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

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

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