此文譯自: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.prototype
,o
的原型上找到了它。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
用來確認一個對象自身是否含有某個特定鍵的屬性,它返回有一個布爾值。調用這個操做符須要兩個參數,O
和P
,前者是一個對象,後者是個屬性鍵的名稱。這個抽象操做符的執行,會通過如下步驟:一、斷言:
Type(O)
是一個對象二、斷言:
IsPropertyKey(P)
爲 true三、令
desc
爲?O.[[GetOwnProperty]](P)
四、若是
desc
是undefined
,返回false
五、返回
true
可是什麼是一個「抽象操做符」?[[ ]]
裏面的內容是什麼?爲何在一個函數以前有一個 ?
,這個「推斷」又是什麼意思呢?
如今,讓咱們找到答案!
讓咱們先來看一對類似的概念,語言類型和規範類型。ECMAScript 規範使用 undefined
、true
、false
,等值 —— 咱們已從 JavaScript 瞭解它們。它們都是「語言值」,也就是規範中定義的語言類型的值。
在規範中也使用了語言內置的值,好比一個內置的數據類型可能包含一個值爲 true
和 false
的字段,相反,JavaScript 引擎一般不會使用這些內置的語言值。例如,若是 JavScript 引擎是使用 C++ 編寫,它可能會使用 c++ 裏面的 true
和 false
,而不是 JavaScript 內部的 true
和 false
。
除了語言類型外,規範中還使用了規範類型,它們是僅僅出如今規範中而沒有出如今 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 Record 是一個規範中的類型(僅僅定義在規範中),JavaScript 引擎並不須要一個相應的內置數據類型。
Completion Record :
名字 | 描述 |
---|---|
[[Type]] | 值能夠是 nomal 、break 、continue 、return 和 throw ,除了 nomal ,其餘都是***中斷完成*** |
[[Value]] | 完成時,產生的值。好比函數的返回值,或者是一個異常(若是拋出) |
[[Target]] | 用來定向轉移的標籤 |
每一個抽象操做符隱式返回一個 Completion Record,儘管它可能僅僅是返回了一個 Boolean 類型,它也會被 Completion Record 包裹着(詳情能夠查看 隱式的 Completion Values)。
注意點1:規範在這方面並不徹底一致。由於存在一些直接返回裸露值的輔助函數,這些函數返回值按照原樣使用,而不是從 Completion Record 中提取出值。一般能夠從上下文中清楚看出。
注意點2:規範的編寫者正在找尋一種能讓 Completion Record 更加明確的方式。
若是一個程序拋出一場,這意味着須要返回一個有特定 [[type]]
的 Completion Record,它的 [[Value]]
是一個含有錯誤信息的對象。到如今爲止,咱們將會忽略 break
、continue
、return
的 [[type]]
。
ReturnIfAbrupt(argument)
將會進行如下步驟:
一、若是參數出錯,返回這個參數
二、把參數賦值給
argument.[[value]]
這就是咱們指望的一個 Completion Record。若是一箇中斷完成了,便當即放回。不然咱們將會從 Completion Record 中取值。
ReturnIfAbrupt
看起來像是一個函數的調用,但實際上並非。它致使返回ReturnIfAbrupt()
的函數返回,而不是 ReturnIfAbrupt
函數自己返回。它的行爲更像是 C 語言中的宏(macro)。
ReturnIfAbrupt
能像下面這樣使用:
一、令
obj
爲Foo()
(obj 爲 Completion Record)二、ReturnIfAbrupt(obj)
三、Bar(obj)(若是進行到這步。obj 從 Completion Record 中提取的值)
所以 ?
的做用既是:?Foo()
等價於 ReturnIfAbrupt(Foo())
。使用簡寫的形式是有用的,咱們沒必要每次都寫捕獲錯誤的代碼。
與此類似,!Foo()
等價於:
一、令
val
爲Foo
二、斷言,
val
不是一個忽然的中斷三、把
val
賦值給val.[[value]]
使用這些知識,咱們能夠寫出 Object.prototype.hasOwnProperty
:
Object.prototype.hasOwnProperty(P)
一、令
P
爲ToPropertyKey(V)
二、若是
P
是一箇中斷完成,返回P
三、令
p.[[value]]
等於p
四、令
O
爲ToObject(O)
五、若是
O
是一箇中斷完成,返回O
六、令
O.[[value]]
等於O
七、令
temp
爲HasOwnProperty(O, P)
八、若是
temp
是中斷完成,返回temp
九、令
temp
爲temp.[[value]]
十、返回
NormalCompletion
與此類似,咱們也能夠重寫出 HasOwnProperty(O, P)
HasOwnProperty(O, P)
一、斷言:
Type(O)
是一個對象二、斷言:
IsPropertyKey(P)
是 true三、令
desc
爲O.[[GetOwnProperty]](P)
四、若是
desc
是一箇中斷完成,返回O
五、令
desc.[[value]]
等於desc
六、若是
desc
是undefined
,返回NormalCompletion(false)
七、返回
NormalCompletion(true)
咱們也能夠重寫不帶 !
的內部方法 [[GetOwnProperty]]
:
O.[[GetOwnProperty]]
一、令
temp
爲OrdinaryGetOwnProperty(O, P)
二、斷言:
temp
不會是一箇中斷完成三、令
temp.[[value]]
等於temp
四、返回
NormalCompletion(temp)
在這裏,咱們假設 temp 是一個全新的臨時變量,不會與其餘任何衝突。
咱們還使用瞭如下知識點:當 return
語句返回除 Completion Record 之外的其餘內容時,它隱式包裝在 NormalCompletion
中。
到如今,咱們已經瞭解了閱讀如 Object.prototype.hasOwnPropert
以及抽象操做符 HasOwnProperty
規範所需的知識點。它們仍然須要委託其餘抽象操做符去工做,基於此博客,咱們應該能瞭解到它們是如何工做的。接下來,咱們將會碰見屬性描述符,它們僅僅另一種規範類型。