js--作用域和作用域鏈

  • 前天被問到作用域鏈的知識,感覺有個大概的認識,但是轉化為語言就無法調理清楚地講述出來,回來后決定惡補功課,在此做個筆記。
  • 注:筆記有部分內容摘抄自其他技術博客,如覺得有被侵權,我會立馬刪除。

變量對象

  • 首先要理解變量對象是什么,代碼在執行時都會經歷先創建再執行的過程,在創建時,就會建立一個變量對象,里面包括arguments、本函數內部聲明的函數、本函數內部聲明的變量、函數形參等。
  • 用圖像表示就是:
Paste_Image.png

再舉個例子:
function outerFun (arg1, arg2) {
var outerV1 = 1
var outerV2 = 2
function innerFun1 () {
var innerV1 = 3;
var innerV2 = 4;
console.log('i am innerFun1...')
}
function innerFun2 () {
console.log('i am innerFun2...')
}
function outerV2 () {
return 'i am outerV2'
}
}
outerFun()


Paste_Image.png

活動對象

  • 個人覺得就是指的變量對象,只不過變量對象是在創建時候的術語,而活動對象是指在執行狀態的對象。

[[scope]]屬性

  • [[scope]]是所有外層函數(執行環境)的變量對象的層級鏈。

  • [[scope]]屬性在函數創建時被存儲,永遠不變,直到函數被銷毀。函數可以不被調用,但該屬性一直存在。

  • 與作用域鏈相比,作用域鏈是活動的執行環境的一個屬性,而[[scope]]是函數的屬性。

  • 舉個例子:
    function add(num1,num2) { var sum = num1 + num2; return sum; }

  • add函數是定義在全局環境里面的,所以在創建時,它的作用域鏈里面的第一個指針會指向一個全局對象,該全局對象包含了所有的全局變量。

Paste_Image.png

作用域鏈

  • 剛剛也說了,作用域鏈是執行環境的一個屬性,當函數開始執行時,就要在執行環境中建立作用域鏈,首先通過復制[[scope]]屬性中的對象,來構建起執行環境的初始作用域。然后將當前函數的活動對象(包含本函數內部的變量、this、arguments等對象)等推入作用域鏈的頂端。
  • 繼續上面的例子:
  • 當開始執行函數add(5,10)時,會創建一個稱為“執行上下文(execution context)”的內部對象,也就是add.execution_context,它是屬于add的一個對象。
  • 這個對象有一個Scope Chain指針指向作用域鏈,這個作用域鏈根據[[scope]]來進行初始化,然后將自己的活動對象也推入作用域鏈。( 該對象包含了該函數環境中的所有局部變量、函數方法、命名參數以及this,arguments等)
Paste_Image.png
  • 函數每執行一次,它的執行上下文都會被重新創建,到函數運行結束時,又再銷毀。

  • 對于在函數內部作為返回值被返回的閉包函數,在其被返回以后,它的作用域鏈就被初始化了,此時包含外層函數的活動對象、全局變量對象,至于它自己的活動對象,是在調用的時候才建立的。最終它的作用域鏈里面會有3個對象,依次是:自己的活動對象、外層行數的活動對象、全局變量對象。

  • 在函數執行過程中,每遇到一個變量,都會經歷一次標識符解析過程以決定從哪里獲取和存儲數據。該過程從作用域鏈頭部,也就是從活動對象開始搜索,查找同名的標識符,如果找到了就使用這個標識符對應的變量,如果沒找到繼續搜索作用域鏈中的下一個對象,如果搜索完所有對象都未找到,則認為該標識符未定義。函數執行過程中,每個標識符都要經歷這樣的搜索過程。
改變作用域鏈
  • 函數每次執行時對應的運行期上下文都是獨一無二的,所以多次調用同一個函數就會導致創建多個運行期上下文,當函數執行完畢,執行上下文會被銷毀。每一個運行期上下文都和一個作用域鏈關聯。一般情況下,在運行期上下文運行的過程中,其作用域鏈只會被 with 語句和 catch 語句影響。
  • with語句是對象的快捷應用方式,用來避免書寫重復代碼。例如:
    function initUI(){
    with(document){
    var bd=body,
    links=getElementsByTagName("a"),
    i=0,
    len=links.length;
    while(i < len){
    update(links[i++]);
    }
    getElementById("btnInit").onclick=function(){
    doSomething();
    };
    }
    }
  • 這里使用width語句來避免多次書寫document,看上去更高效,實際上產生了性能問題。
  • 當代碼運行到with語句時,運行期上下文的作用域鏈臨時被改變了。一個新的可變對象被創建,它包含了參數指定的對象的所有屬性。這個對象將被推入作用域鏈的頭部,這意味著函數的所有局部變量現在處于第二個作用域鏈對象中,因此訪問代價更高了。如下圖所示:
Paste_Image.png
  • 因此在程序中應避免使用with語句,在這個例子中,只要簡單的把document存儲在一個局部變量中就可以提升性能。

  • 另外一個會改變作用域鏈的是try-catch語句中的catch語句。當try代碼塊中發生錯誤時,執行過程會跳轉到catch語句,然后把異常對象推入一個可變對象并置于作用域的頭部。在catch代碼塊內部,函數的所有局部變量將會被放在第二個作用域鏈對象中。示例代碼:

  •   try{
      doSomething();
      }catch(ex){
      alert(ex.message); //作用域鏈在此處改變
      }
    
  • 請注意,一旦catch語句執行完畢,作用域鏈機會返回到之前的狀態。try-catch語句在代碼調試和異常處理中非常有用,因此不建議完全避免。你可以通過優化代碼來減少catch語句對性能的影響。一個很好的模式是將錯誤委托給一個函數處理,例如:

  • try{
        doSomething();
    }catch(ex){
        handleError(ex); //委托給處理器方法
    }
    
  • 優化后的代碼,handleError方法是catch子句中唯一執行的代碼。該函數接收異常對象作為參數,這樣你可以更加靈活和統一的處理錯誤。由于只執行一條語句,且沒有局部變量的訪問,作用域鏈的臨時改變就不會影響代碼性能了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容