JavaScript 中的活躍對象(AO)淺談及導讀

目前市面上不少文章一大抄,在現在 ES202+ 的年代,還在摘抄着 ES3 的原文。html

Every execution context has associated with it a variable object. Variables and functions declared in the source text are added as properties of the variable object. For function code, parameters are added as properties of the variable object.

而後將其與「面試官」綁在一塊兒。其實在書面理解上沿用活躍對象的概念沒什麼問題,可是照抄原文又不指明出處,就會讓人誤覺得現在的規範中也還定義了活躍對象這一律念。其實上引文中包含了活躍對象(Activation Object, AO,有時也稱活動對象、激活對象)與可變對象(Variable Object, VO,有時也稱變量對象)的內容,摘抄自 ECMAScript 3 Spec 的兩處並組裝起來。面試

今天,這裏與你們一塊兒淺嘗一下 JavaScript 中的活躍對象。網絡

ECMAScript 1 / 3

在 ECMAScript 1 和 ECMAScript 3 中,的確是有着關於活躍對象的定義。數據結構

當控制進入函數代碼的執行上下文時,建立一個活動對象並將它與該執行上下文相關聯, 並使用一個名爲 arguments、特徵爲 { DontDelete } 的屬性初始化該對象。該屬性的初始值是稍後將要描述的一個參數對象。 閉包

接下來,這個活動對象將被用做變量初始化的可變對象。 ecmascript

活動對象純粹是一種規範性機制,在 ECMAScript 訪問它是不可能的。只能訪問其成員而非該活動對象自己。對一個基對象爲活動對象的引用值應用調用運算符時,此次調用的 this 值 爲 null函數

——ECMAScript Language Specification 262 Edition 3 Final, 10.1.6 活躍對象this

但也僅限於 ECMAScript 1 和 3 了。咱們如今在網上(尤爲是中文搜索環境中)獲取到的關於活躍對象和可變對象(Variable Object)的文章,大多都是爲咱們描述的 ECMAScript 1 和 3,早已過期。es5

若是你們對這塊內容仍然感興趣(實際上我也建議你們感興趣),能夠參閱:spa

ECMAScript 5+

在 ES5 及以後的 ES 版本,已經不存在活躍對象(AO)及一系列周邊內容的概念了。取而代之,是一個叫詞法環境(Lexical Environments)的定義。

也就是說,嚴謹來說,現代的 ECMAScript 早已沒有了活躍對象這一律念,因此當網絡上文章中「面試官跟你聊起 AO」這些內容出現的時候,其實就是「市面文章一大抄」的體現。他們還會有理有據地把 ECMAScript Spec 原文給你列出來(參照文首的摘抄)。

關於詞法環境,你們能夠參閱:

這裏就不贅述了。

廣義的活躍對象

通過上面兩節內容,咱們能夠知道,活躍對象是 ECMAScript 1 / 3 中的內容。後續的版本中,其就不復存在了。可是活躍對象這個概念就不能再被提起了嗎?

其實也不是,它對應的概念仍是能夠延續下來的。只不過不能讓人誤覺得現代 ECMAScript 中還有其定義,咱們如今再聊起活躍對象時,應該知道它只是廣義的抽象,而再也不是狹義的定義了。廣義的活躍對象在不一樣的場景下也能夠有不一樣的名字,如活躍記錄(Activation Record)、棧幀(Stack Frame)等。

每當函數被調用的時候,其都會建立一個活躍對象。該對象對開發者不可見,是一個隱藏的數據結構,其中包含了一些函數在執行時必要的信息和綁定,以及返回值的地址等等。

在 C 語言中,這個對象會在一個棧中被分配生成。當函數返回的時候,該對象會被銷燬(或者出棧)。

你看,此處「活躍對象」被引伸到 C 語言了。它指的是一個抽象的存在,意爲棧幀(Stack Frame)。

JavaScript 與 C 語言不一樣,它是從堆中分配該對象。且這個活躍對象並不會在函數返回時被自動銷燬,它的生命週期與普通對象的垃圾回收機制相似,是根據引用數量決定的。

一個活躍對象包含:

  • 對應函數對象的引用;
  • 調用者對應的活躍對象,用於 return 以後的控制權轉移;
  • 調用完畢以後用於繼續執行後續邏輯的恢復信息,它一般是一個將在函數調用完畢以後當即要執行的指令的地址;
  • 函數對應的形參,從實參初始化而來;
  • 函數中的變量,以 undefined 進行初始化;
  • 函數用於計算複雜表達式的臨時變量;
  • this,若是函數做爲一個方法被調用,那麼 this 一般就是它的宿主對象。

其實 ES5+ 以後的廣義「活躍對象」就是對於 ES 1 / 3 定義的活躍對象的一個擴展,並將其應用到了詞法環境中。

至此爲止,關於「活躍對象」的淺析就足矣。當下環境中,咱們不是不能再談論「活躍對象」,而是不能亂談,還談得有鼻子有眼的。現現在的「活躍對象」是一個相似於活躍記錄和棧幀的廣義抽象概念。

否則,仍舊用老舊的文章去回答所謂「面試官」的問題,頗有可能被刷掉哦。

閉包

閉包也是老生常談的一個概念。爲何在這篇文章中要提起這麼個看起來八竿子打不着的概念呢?

閉包一直沒有一個很是嚴謹的定義。如:

閉包就是可以讀取其餘函數內部變量的函數。

閉包就是可以讀取外層函數變量的函數。

等等等等。

再例如:

「函數」和「函數內部能訪問到的變量」(也叫環境)的總和,就是一個閉包。

上述的解釋也都是我從網上的各文章中摘抄出來的。其實理解起來很容易,可是語言描述出來並不那麼嚴謹。你們知道那麼回事就行了。

不過,在咱們有了廣義活躍對象以後,咱們能夠從另外一個角度來定義閉包了。怎麼說呢?函數是可嵌套的。當一個嵌套的函數對象被建立時,它會包含一個外層函數對象所對應的活躍對象引用

有了這層關係,閉包就好定義了:

一個擁有外層函數對象所對應的活躍對象引用的函數對象就被稱爲閉包。

言簡意賅。雖然不至於像「能讀取外層函數中的變量」那樣親民樸實,但也很是言簡了。同時,有了「活躍對象」做爲大前提,已經幫忙作了不少前提條件定義,因此這個定義也能達到意賅的效果。

之後再面試起什麼是閉包,你們能夠嘗試從這個角度解釋哦。前提是你真的懂了,不要到時候又被面試官給繞進去了。

相關文章
相關標籤/搜索