做者:Marja Hölttä翻譯:瘋狂的技術宅前端
原文:https://v8.dev/blog/understan...程序員
未經容許嚴禁轉載面試
即使你對 JavaScript 很瞭解,可是去閱讀 ECMAScript 語言規範,或簡稱爲 ECMAScript 規範也會使人生畏。至少這是我在第一次開始閱讀時的感覺。算法
讓咱們從一個具體的例子開始,而後經過遍歷整個規範以瞭解它。如下代碼演示了 Object.prototype.hasOwnProperty
的用法:segmentfault
const o = { foo: 1 }; o.hasOwnProperty('foo'); // true o.hasOwnProperty('bar'); // false
在例子中,對象 o
沒有名爲 hasOwnProperty
的屬性,讓咱們沿着原型鏈去尋找。能夠在 o
的原型 Object.prototype
中找到它。服務器
爲了描述 Object.prototype.hasOwnProperty
的工做方式,該規範使用了相似僞代碼的描述:微信
Object.prototype.hasOwnProperty(V)
多線程當使用參數
V
調用hasOwnProperty
方法時,將執行如下步驟:框架
- 使
P
爲? ToPropertyKey(V)
。- 使
O
爲? ToObject(this value)
。- 返回
? HasOwnProperty(O, P)
。
和ecmascript
抽象操做
HasOwnProperty
用於肯定對象是否具備帶有指定屬性鍵的本身的屬性。其返回一個布爾值。該操做使用參數O
和P
調用,其中O
是對象,而P
是屬性鍵。此抽象操做執行如下步驟:
- 斷言:
Type(O)
是Object
。- 斷言:
IsPropertyKey(P)
爲true
。- 使
desc
爲? O.[[GetOwnProperty]](P)
。- 若是
desc
爲undefined
,則返回false
。- 返回
true
。
但什麼是「抽象操做」呢? [[]]
裏面有什麼東西?爲何在函數前面有一個?
?這些斷言又是什麼意思?
快來找出答案吧!
讓咱們從看上去熟悉的東西開始。規範使用了咱們從 JavaScript 中已經知道的值,例如 undefined
,true
和 false
。它們都是 語言值,即規範中所定義的 語言類型的值。
規範還在內部使用語言值,例如,內部數據類型可能包含一個字段,其可能值爲 true
和 false
。相反,JavaScript 引擎一般在內部不使用語言值。例如,若是 JavaScript 引擎是用 C ++ 編寫的,則一般會使用 C++ 的true
和 false
(而不是 JavaScript 的 true
和 false
的內部表示)。
除語言類型外,規範還使用規格類型,這些類型僅在規範中出現,但不屬於 JavaScript 語言。 JavaScript 引擎不須要(但能夠)實現它們。在本文中,咱們將瞭解規範類型 Record(及其子類型 Completion Record)。
抽象操做是 ECMAScript 規範中定義的函數;定義它們是爲了簡潔地編寫規範。 JavaScript 引擎沒必要將其做爲單獨的函數實如今引擎內部。不能從 JavaScript 直接調用它們。
內部插槽和內部方法使用包含在 [[]]
中的名稱。
內部插槽是 JavaScript 對象或規範類型的數據成員。它們被用於存儲對象的狀態。內部方法是 JavaScript 對象的成員函數。
例如,每一個 JavaScript 對象都有一個內部插槽 [[Prototype]]
和一個內部方法 [[GetOwnProperty]]
。
沒法從 JavaScript 訪問內部插槽和方法。例如,你沒法訪問 o.[[Prototype]]
或調用 o.[[GetOwnProperty]]()
。 JavaScript 引擎能夠實現它們以供內部使用,但並非必須的。
有時內部方法委託相似名稱的抽象操做,例如在普通對象的 [[GetOwnProperty]]
中:
[[GetOwnProperty\]](P)
當使用屬性鍵
P
調用O
的[[GetOwnProperty]]
內部方法時,將執行如下步驟:返回
! OrdinaryGetOwnProperty(O,P)
。
(咱們將在下一章中找到感嘆號的含義。)
OrdinaryGetOwnProperty
不是內部方法,由於它沒有與任何對象相關聯;而是將對其進行操做的對象做爲參數傳遞。
由於 OrdinaryGetOwnProperty
對普通對象起做用,因此被稱爲「普通」。 ECMAScript 對象能夠是普通或外部的。普通對象必須具備一組被稱爲基本內部方法的方法的默認行爲。若是某個對象偏離默認行爲,則該對象是外部的。
最著名的外部對象是 Array,由於其 length
屬性以非默認方式運行:設置 length
屬性能夠從 Array
中刪除元素。
基本的內部方法是 https://tc39.es/ecma262/#table-5 中列出的方法。
問號和感嘆號是怎麼回事?要了解它們,咱們須要研究完成記錄(Completion Records)!
完成記錄是一種規範類型(僅出於規範目的而定義)。 JavaScript 引擎沒必要具備相應的內部數據類型。
完成記錄是一種「記錄」——一種具備一組固定的命名字段的數據類型。完成記錄包含三個字段:
名稱 | 描述 |
---|---|
[[Type]] |
normal , break , continue , return 或 throw 中的一個。 除 normal 之外的全部其餘類型都是忽然完成」( abrupt completions) |
[[Value]] |
完成發生時產生的值,例如:函數的返回值或異常(若是引起了異常的話)。 |
[[Target]] |
用於定向控制轉移(與本文無關)。 |
每一個抽象操做都隱式返回完成記錄。即便看起來抽象操做會返回一個簡單的類型(例如 Boolean),它也將被隱式包裝爲類型爲 normal
的完成記錄(請參見隱式完成值。
注1:規格在這方面並不徹底一致;有些輔助函數返回裸值,而且其返回值按原樣使用,而無需從「完成記錄」中提取值。一般從上下文中能夠清楚地看出這一點。
注2:規範制定人員正在研究使「完成記錄」的處理更加明確。
若是算法拋出異常,則意味着返回帶有 [[Type]]
throw
的完成記錄,而 [[Value]]
是異常對象。如今,咱們將忽略 break
, continue
和 return
類型。
ReturnIfAbrupt(argument)
意味着採起如下步驟:
- 若是
argument
忽然出現,則返回argument
- 將
argument
設置爲argument.[[Value]]
也就是說,咱們檢查完成記錄;若是是忽然完成,會當即返回。不然從完成記錄中提取值。
ReturnIfAbrupt
可能看起來像一個函數調用,但事實並不是如此。它是致使返回 ReturnIfAbrupt()
的函數返回的緣由,而不是返回 ReturnIfAbrupt
函數自己的函數。它的行爲更像是 C 語言中的宏。
ReturnIfAbrupt
能夠這樣使用:
- 令
obj
爲Foo()
。 (obj
是 Completion Record。)ReturnIfAbrupt(obj)
Bar(obj)
. (若是仍在此處,則obj
是從 Completion Record 中提取的值)
如今問號開始起做用:? Foo()
等同於 ReturnIfAbrupt(Foo())
。使用簡寫很實用:咱們沒必要每次都明確編寫錯誤處理代碼。
一樣, Let val be ! Foo()
等效於:
- 使
val
爲Foo()
- 斷言:
val
不是忽然完成的- 將
val
設置爲val.[[Value]]
利用這些知識,咱們能夠這樣重寫 Object.prototype.hasOwnProperty
:
Object.prototype.hasOwnProperty(P)
- 使
P
爲ToPropertyKey(V)
- 若是
P
是忽然完成的,則返回P
- 將
P
設置爲P.[[Value]]
- 令
O
爲ToObject(this value)
- 若是
O
是忽然完成的,則返回O
- 將
O
設置爲O.[[Value]]
- 使
temp
爲HasOwnProperty(O, P)
- 若是
temp
是忽然完成的,則返回temp
- 使
temp
爲temp.[[Value]]
- 返回
NormalCompletion(temp)
…咱們能夠這樣重寫 HasOwnProperty
:
HasOwnProperty(O, P)
- 斷言:
Type(O)
爲Object
- 斷言:
IsPropertyKey(P)
爲true
.- 令
desc
爲O.[[GetOwnProperty]](P)
- 若是
desc
是忽然完成,則返回desc
- 將
desc
設置爲desc.[[Value]]
- 若是
desc
是undefined
, 則返回NormalCompletion(false)
- 返回
NormalCompletion(true)
咱們也能夠重寫不帶感嘆號的 [[GetOwnProperty]]
內部方法:
O.[[GetOwnProperty]]
- 令
temp
爲OrdinaryGetOwnProperty(O, P)
- 斷言:
temp
不是忽然完成- 使
temp
爲temp.[[Value]]
- 返回
NormalCompletion(temp)
在這裏,咱們假設 temp
是一個全新的臨時變量,不會與其餘任何衝突。
咱們還使用瞭如下知識:當 return 語句返回除 Completion Record 之外的其餘內容時,它隱式包裝在 NormalCompletion
中。
Return ? Foo()
規範使用 Return ? Foo()
——爲何有問號?
Return ? Foo()
擴展爲:
- 使
temp
爲Foo()
- 若是
temp
是忽然完成的,則返回temp
- 將
temp
設置爲temp.[[Value]]
- 返回
NormalCompletion(temp)
與 Return Foo()
相同;對於忽然和正常完成,其行爲方式相同。
Return ? Foo()
僅出於編輯緣由而使用,以使其可以更明確地表達 Foo 返回完成記錄。
規範中的主張斷言了算法的不變條件。爲了清楚起見,添加了它們,但沒有對實現添加任何要求——實現中不須要檢查它們。
咱們已經創建了閱讀規範所需的知識,如 Object.prototype.hasOwnProperty
之類的簡單方法和諸如 HasOwnProperty
之類的抽象操做。它們仍然委託其餘抽象操做,可是基於本文,咱們應該可以弄清楚它們的做用。咱們將遇到屬性描述符,這是另外一種規範類型。
如何閱讀 ECMAScript 規範(https://timothygu.me/es-howto/):該教程從一個稍微不一樣的角度涵蓋了本文中的許多內容。