摘要:全局和上下文中的作用域鏈這里不一定很有趣,但必須要提示一下。全局上下文的作用域鏈僅包含全局對象。代碼的上下文與當前的調用上下文擁有同樣的作用域鏈。代碼執行時對作用域鏈的影響在中,在代碼執行階段有兩個聲明能修改作用域鏈。
1 定義
我們已經知道一個執行上下文中的數據(參數,變量,函數)作為屬性存儲在變量對象中。
也知道變量對象是在每次進入上下文是創建并填入初始值,值的更新出現在代碼執行階段。
作用域鏈就是這些變量對象的鏈表。
讓我們看一下和作用域相關的上下文結構
VO是當前上下文的變量對象,重點是Scope屬性,Scope = VO+[[scope]]。其中[[scope]]為所有父上下文變量對象的鏈表。
activeExecutionContext = { VO: {...}, // or AO this: thisValue, Scope: [ // Scope chain // 所有變量對象的列表 // for identifiers lookup ] };
函數的生命周期分為創建和激活。
2 函數創建階段var x = 10; function foo() { var y = 20; alert(x + y); } foo(); // 30
此前,我們僅僅談到有關當前上下文的變量對象。這里,我們看到變量“y”在函數“foo”中定義(意味著它在foo上下文的AO中),但是變量“x”并未在“foo”上下文中定義,相應地,它也不會添加到“foo”的AO中。乍一看,變量“x”相對于函數“foo”根本就不存在;但正如我們在下面看到的——也僅僅是“一瞥”,我們發現,“foo”上下文的活動對象中僅包含一個屬性--“y”。
fooContext.AO = { y: undefined // undefined – 進入上下文的時候是20 – at activation };
函數“foo”如何訪問到變量“x”?理論上函數應該能訪問一個更高一層上下文的變量對象。實際上它正是這樣,這種機制是通過函數內部的[[scope]]屬性來實現的。
[[scope]]是所有父變量對象的層級鏈,處于當前函數上下文之上,在函數創建時存于其中。
注意這重要的一點--[[scope]]在函數創建時被存儲--靜態(不變的),永遠永遠,直至函數銷毀。即:函數可以永不調用,但[[scope]]屬性已經寫入,并存儲在函數對象中。
3 函數激活階段正如在定義中說到的,進入上下文創建AO/VO之后,上下文的Scope屬性(變量查找的一個作用域鏈)作如下定義:
Scope = AO|VO + [[Scope]]
上面代碼的意思是:活動對象是作用域數組的第一個對象,即添加到作用域的前端。
Scope = [AO].concat([[Scope]]);
這個特點對于標示符解析的處理來說很重要。
標示符解析是一個處理過程,用來確定一個變量(或函數聲明)屬于哪個變量對象。
標識符解析過程包含與變量名對應屬性的查找,即作用域中變量對象的連續查找,從最深的上下文開始,繞過作用域鏈直到最上層。
這樣一來,在向上查找中,一個上下文中的局部變量較之于父作用域的變量擁有較高的優先級。萬一兩個變量有相同的名稱但來自不同的作用域,那么第一個被發現的是在最深作用域中。
4 閉包在ECMAScript中,閉包與函數的[[scope]]直接相關,正如我們提到的那樣,[[scope]]在函數創建時被存儲,與函數共存亡。實際上,閉包是函數代碼和其[[scope]]的結合。
因為閉包函數在創建的時候就創建了父級的變量對象鏈表,也就是父級作用域鏈, 然后閉包函數再訪問父級作用域鏈中的變量,導致父級函數執行完畢后仍然不能釋放執行上下文的情況。
5 通過構造函數創建的函數的[[scope]]在上面的例子中,我們看到,在函數創建時獲得函數的[[scope]]屬性,通過該屬性訪問到所有父上下文的變量。但是,這個規則有一個重要的例外,它涉及到通過函數構造函數創建的函數。
var x = 10; function foo() { var y = 20; function barFD() { // 函數聲明 alert(x); alert(y); } var barFE = function () { // 函數表達式 alert(x); alert(y); }; var barFn = Function("alert(x); alert(y);"); barFD(); // 10, 20 barFE(); // 10, 20 barFn(); // 10, "y" is not defined } foo();
我們看到,通過函數構造函數(Function constructor)創建的函數“bar”,是不能訪問變量“y”的。但這并不意味著函數“barFn”沒有[[scope]]屬性(否則它不能訪問到變量“x”)。問題在于通過函構造函數創建的函數的[[scope]]屬性總是唯一的全局對象。考慮到這一點,如通過這種函數創建除全局之外的最上層的上下文閉包是不可能的。
6 全局和eval上下文中的作用域鏈這里不一定很有趣,但必須要提示一下。全局上下文的作用域鏈僅包含全局對象。代碼eval的上下文與當前的調用上下文(calling context)擁有同樣的作用域鏈。
globalContext.Scope = [ Global ]; evalContext.Scope === callingContext.Scope;7 代碼執行時對作用域鏈的影響
在ECMAScript 中,在代碼執行階段有兩個聲明能修改作用域鏈。這就是with聲明和catch語句。它們添加到作用域鏈的最前端,對象須在這些聲明中出現的標識符中查找。如果發生其中的一個,作用域鏈簡要的作如下修改:
Scope = withObject|catchObject + AO|VO + [[Scope]]
eval代碼運行的字符串利用當前調用的上下文,并且能夠修改當前上下文中的變量對象,也就是說eval內和eval外的代碼一樣,只是不能變量提升,執行起來是一樣的。
with代碼塊和catch代碼塊都改變了作用域鏈,但是在他們代碼塊中聲明的變量,也存在了函數作用域中,只是with的對象和catch對象外部不能訪問而已。
eval("var x=3"); console.log(x); //3 with (obj1) { var innner ="inner"; console.log(a); //1 console.log(b); //2 } console.log(innner); // inner 仍能訪問 console.log(a) //訪問不到 try { throw new Error("error") } catch (e) { var cat = 2; } console.log(cat); //2 仍能訪問
英文原文:http://dmitrysoshnikov.com/ec...
本文絕大部分內容來自上述地址,僅做少許修改
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86609.html
摘要:代碼執行階段在這個階段會生成三個重要的東西變量對象,作用域鏈變量對象在函數上下文中,我們用活動對象來表示變量對象。這時候執行上下文的作用域鏈,我們命名為至此,作用域鏈創建完畢。 好久沒更新文章了,這一憋就是一個大的。說起js中的概念,執行上下文和作用域應該是大家最容易混淆的,你說混淆就混淆吧,其實大多數人在開發的時候不是很關注這兩個名詞,但是這里面偏偏還夾雜好多其他的概念--變量提升啊...
摘要:之前一篇文章我們詳細說明了變量對象,而這里,我們將詳細說明作用域鏈。而的作用域鏈,則同時包含了這三個變量對象,所以的執行上下文可如下表示。下圖展示了閉包的作用域鏈。其中為當前的函數調用棧,為當前正在被執行的函數的作用域鏈,為當前的局部變量。 showImg(https://segmentfault.com/img/remote/1460000008329355);初學JavaScrip...
摘要:最后重點理解結論箭頭函數的,總是指向定義時所在的對象,而不是運行時所在的對象。輸出,箭頭函數不會綁定所以傳入指向無效。原因是,要徹底理解應該是建立在已經大致理解了中的執行上下文,作用域作用域鏈,閉包,變量對象,函數執行過程的基礎上。 本文共 2025 字,看完只需 8 分鐘 概述 前面的文章講解了 JavaScript 中的執行上下文,作用域,變量對象,this 的相關原理,但是我...
摘要:理解了這句話,我們就可以來看閉包了閉包前面說過,函數可以訪問函數作用域鏈中的變量,但如果我們想在函數外訪問函數內卻不行了。 不管是閉包還是this關鍵字,都是困擾JS初學者的比較難懂的東西,如果你對它們的認識還不足夠清晰,那么現在就一起把它們掌握掉。還是那句話,我們從最基本的開始,建立起一個非常清晰的知識結構,好了,開始吧 ? 閉包 當然我們今天說的是javascript里的閉包。要學...
摘要:理解作用域高級程序設計中有說到對象是在運行時基于函數的執行環境綁定的在全局函數中,等于,而當函數被作為某個對象調用時,等于那個對象。指向與匿名函數沒有關系如果函數獨立調用,那么該函數內部的,則指向。 理解this作用域 《javascript高級程序設計》中有說到: this對象是在運行時基于函數的執行環境綁定的:在全局函數中,this等于window,而當函數被作為某個對象調用時,t...
閱讀 623·2023-04-26 01:53
閱讀 2749·2021-11-17 17:00
閱讀 2880·2021-09-04 16:40
閱讀 1983·2021-09-02 15:41
閱讀 830·2019-08-26 11:34
閱讀 1222·2019-08-26 10:16
閱讀 1335·2019-08-23 17:51
閱讀 815·2019-08-23 16:50