理解 ECMAScript 規範(一)

此文譯自:v8.dev/blog/unders…c++

在這篇文章中,爲咱們將會使用規範中的一個小的功能來作爲切入點,從中去嘗試理解一些特殊操做符,讓咱們開始吧。數組

前言

即便你瞭解 JavaScript,閱讀它的規範(ECMAScript 語言規範簡稱 ECMAScript 規範) 也是會使人生畏的。至少這是我第一次閱讀時的感覺。ecmascript

讓咱們經過一個具體的示例開始。下面的代碼演示了 Object.prototype.hasOwnProperty 的使用:函數

const o = { foo: 1 };
o.hasOwnProperty('foo'); // true
o.hasOwnProperty('bar'); // false
複製代碼

在這個例子中,o 沒有 hasOwnProperty 屬性,所以咱們會在它的原型鏈上尋找 hasOwnProperty 屬性,最終咱們在 Object.prototypeo 的原型上找到了它。this

爲了描述 Object.prototype.hasOwnProperty 是怎麼工做的,ECMAScript 規範使用了僞代碼來描述它:spa

Object.prototype.hasOwnProperty(v)prototype

當帶參數 v 調用 hasOwnProperty,會通過如下步驟code

一、令 P?ToPropertyKey(V).orm

二、令 O?ToObject(this value).cdn

三、返回 ?HasOwnProperty(O, P).

以及:

HasOwnProperty

抽象的操做符 HasOwnProperty 用來確認一個對象自身是否含有某個特定鍵的屬性,它返回有一個布爾值。調用這個操做符須要兩個參數,OP,前者是一個對象,後者是個屬性鍵的名稱。這個抽象操做符的執行,會通過如下步驟:

一、斷言:Type(O) 是一個對象

二、斷言: IsPropertyKey(P) 爲 true

三、令 desc?O.[[GetOwnProperty]](P)

四、若是 descundefined,返回 false

五、返回 true

可是什麼是一個「抽象操做符」?[[ ]] 裏面的內容是什麼?爲何在一個函數以前有一個 ?,這個「推斷」又是什麼意思呢?

如今,讓咱們找到答案!

語言類型和規範類型

讓咱們先來看一對類似的概念,語言類型和規範類型。ECMAScript 規範使用 undefinedtruefalse,等值 —— 咱們已從 JavaScript 瞭解它們。它們都是「語言值」,也就是規範中定義的語言類型的值。

在規範中也使用了語言內置的值,好比一個內置的數據類型可能包含一個值爲 truefalse 的字段,相反,JavaScript 引擎一般不會使用這些內置的語言值。例如,若是 JavScript 引擎是使用 C++ 編寫,它可能會使用 c++ 裏面的 truefalse,而不是 JavaScript 內部的 truefalse

除了語言類型外,規範中還使用了規範類型,它們是僅僅出如今規範中而沒有出如今 JavaScript 中的類型。JavaScript 引擎沒必要(固然也能夠)實現它們。在這篇文章中,咱們將會去了解其中一個規範類型 -- Record 以及它的子類型 Completion Record

抽象操做符

抽象操做符是規範中定義的函數,定義它們是爲了更加簡潔的書寫規範。JavaScript 引擎也沒必要把它們作爲一個單獨的內部函數來實現它們。在 JavaScript 中,它們不能被調用。

內部的插槽和函數

爲了更好的書寫規範,內部的插槽和函數被定義,它們一般被包裹在 [[]] 中。

內部插槽是 JavaScript 對象或者是規範類型中的數據成員,它們用來存儲對象的狀態。內部的方法是 JavaScript 對象方法中的成員。

例如每一個 JavaScript 對象都有一個內部插槽 [[Prototype]] 以及內部方法 [[GetOwnProperty]]

內部插槽和內部方法在 JavaScript 不可被訪問,例如你不能訪問 o.[[Prototype]] 或者調用 o.[[GetOwnProperty]](),JavaScript 引擎能夠實現它們供本身內部使用,但這不是必須的。

有些時候,內部的方法功能將會委託給一個名字類似的抽象操做符,好比普通對象中的 [[GetOwnProperty]]

[[GetOwnProperty]](p)

當帶有參數 P 調用 O 內部的方法 [[GetOwnProperty]] 時,會進行如下步驟:

一、返回 !OrdinaryGetOwnProperty(O, P)

(將會在下一章節解釋 ! 的意思)

OrdinaryGetOwnProperty 並非一個內部的方法,由於它與任何對象都毫無干系,取而代之,這個對象作爲參數被傳入這個操做符。

OrdinaryGetOwnProperty 被稱之爲***普通的(ordinary)***,由於它操做普通的對象。ECMAScript 對象能夠是普通對象,也能夠是變異的對象(exotic),普通對象必須擁有一些默認行爲,好比一組內部方法。若是一個對象偏離這些默認行爲,它就是變異的。

最廣爲人知變異對象是 Array,由於它的長度屬性以非默認方式運行 —— 設置長度屬性,可以從數組中移除一個元素。

基礎的內部方法列表能夠從看到。

Completion records

那麼 ?! 是什麼意思了?爲了理解它們,咱們必須來看看 Completion records

Completion Record 是一個規範中的類型(僅僅定義在規範中),JavaScript 引擎並不須要一個相應的內置數據類型。

Completion Record :

名字 描述
[[Type]] 值能夠是 nomalbreakcontinuereturnthrow,除了 nomal,其餘都是***中斷完成***
[[Value]] 完成時,產生的值。好比函數的返回值,或者是一個異常(若是拋出)
[[Target]] 用來定向轉移的標籤

每一個抽象操做符隱式返回一個 Completion Record,儘管它可能僅僅是返回了一個 Boolean 類型,它也會被 Completion Record 包裹着(詳情能夠查看 隱式的 Completion Values)。

注意點1:規範在這方面並不徹底一致。由於存在一些直接返回裸露值的輔助函數,這些函數返回值按照原樣使用,而不是從 Completion Record 中提取出值。一般能夠從上下文中清楚看出。

注意點2:規範的編寫者正在找尋一種能讓 Completion Record 更加明確的方式。

若是一個程序拋出一場,這意味着須要返回一個有特定 [[type]] 的 Completion Record,它的 [[Value]] 是一個含有錯誤信息的對象。到如今爲止,咱們將會忽略 breakcontinuereturn[[type]]

ReturnIfAbrupt(argument) 將會進行如下步驟:

一、若是參數出錯,返回這個參數

二、把參數賦值給 argument.[[value]]

這就是咱們指望的一個 Completion Record。若是一箇中斷完成了,便當即放回。不然咱們將會從 Completion Record 中取值。

ReturnIfAbrupt 看起來像是一個函數的調用,但實際上並非。它致使返回ReturnIfAbrupt() 的函數返回,而不是 ReturnIfAbrupt 函數自己返回。它的行爲更像是 C 語言中的宏(macro)。

ReturnIfAbrupt 能像下面這樣使用:

一、令 objFoo()(obj 爲 Completion Record)

二、ReturnIfAbrupt(obj)

三、Bar(obj)(若是進行到這步。obj 從 Completion Record 中提取的值)

所以 ? 的做用既是:?Foo() 等價於 ReturnIfAbrupt(Foo())。使用簡寫的形式是有用的,咱們沒必要每次都寫捕獲錯誤的代碼。

與此類似,!Foo() 等價於:

一、令 valFoo

二、斷言,val 不是一個忽然的中斷

三、把 val 賦值給 val.[[value]]

使用這些知識,咱們能夠寫出 Object.prototype.hasOwnProperty

Object.prototype.hasOwnProperty(P)

一、令 PToPropertyKey(V)

二、若是 P 是一箇中斷完成,返回 P

三、令 p.[[value]] 等於 p

四、令 OToObject(O)

五、若是 O 是一箇中斷完成,返回 O

六、令 O.[[value]] 等於 O

七、令 tempHasOwnProperty(O, P)

八、若是 temp 是中斷完成,返回 temp

九、令 temptemp.[[value]]

十、返回 NormalCompletion

與此類似,咱們也能夠重寫出 HasOwnProperty(O, P)

HasOwnProperty(O, P)

一、斷言:Type(O) 是一個對象

二、斷言:IsPropertyKey(P) 是 true

三、令 descO.[[GetOwnProperty]](P)

四、若是 desc 是一箇中斷完成,返回 O

五、令 desc.[[value]] 等於 desc

六、若是 descundefined,返回 NormalCompletion(false)

七、返回 NormalCompletion(true)

咱們也能夠重寫不帶 ! 的內部方法 [[GetOwnProperty]]

O.[[GetOwnProperty]]

一、令 tempOrdinaryGetOwnProperty(O, P)

二、斷言:temp 不會是一箇中斷完成

三、令 temp.[[value]] 等於 temp

四、返回 NormalCompletion(temp)

在這裏,咱們假設 temp 是一個全新的臨時變量,不會與其餘任何衝突。

咱們還使用瞭如下知識點:當 return 語句返回除 Completion Record 之外的其餘內容時,它隱式包裝在 NormalCompletion 中。

總結

到如今,咱們已經瞭解了閱讀如 Object.prototype.hasOwnPropert 以及抽象操做符 HasOwnProperty 規範所需的知識點。它們仍然須要委託其餘抽象操做符去工做,基於此博客,咱們應該能瞭解到它們是如何工做的。接下來,咱們將會碰見屬性描述符,它們僅僅另一種規範類型。

相關文章
相關標籤/搜索