瞭解ECMAScript規範,第1部分

做者: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 方法時,將執行如下步驟:框架

  1. 使 P? ToPropertyKey(V)
  2. 使 O? ToObject(this value)
  3. 返回 ? HasOwnProperty(O, P)

ecmascript

HasOwnProperty(O, P)

抽象操做 HasOwnProperty 用於肯定對象是否具備帶有指定屬性鍵的本身的屬性。其返回一個布爾值。該操做使用參數 OP 調用,其中 O 是對象,而 P 是屬性鍵。此抽象操做執行如下步驟:

  1. 斷言: Type(O)Object
  2. 斷言: IsPropertyKey(P)true
  3. 使 desc? O.[[GetOwnProperty]](P)
  4. 若是 descundefined,則返回 false
  5. 返回 true

但什麼是「抽象操做」呢? [[]] 裏面有什麼東西?爲何在函數前面有一個? ?這些斷言又是什麼意思?

快來找出答案吧!

語言類型和規範類型

讓咱們從看上去熟悉的東西開始。規範使用了咱們從 JavaScript 中已經知道的值,例如 undefinedtruefalse。它們都是 語言值,即規範中所定義的 語言類型的值。

規範還在內部使用語言值,例如,內部數據類型可能包含一個字段,其可能值爲 truefalse。相反,JavaScript 引擎一般在內部不使用語言值。例如,若是 JavaScript 引擎是用 C ++ 編寫的,則一般會使用 C++ 的truefalse(而不是 JavaScript 的 truefalse 的內部表示)。

除語言類型外,規範還使用規格類型,這些類型僅在規範中出現,但不屬於 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)

問號和感嘆號是怎麼回事?要了解它們,咱們須要研究完成記錄(Completion Records)

完成記錄是一種規範類型(僅出於規範目的而定義)。 JavaScript 引擎沒必要具備相應的內部數據類型。

完成記錄是一種「記錄」——一種具備一組固定的命名字段的數據類型。完成記錄包含三個字段:

名稱 描述
[[Type]] normal, break, continue, returnthrow 中的一個。 除 normal 之外的全部其餘類型都是忽然完成」( abrupt completions)
[[Value]] 完成發生時產生的值,例如:函數的返回值或異常(若是引起了異常的話)。
[[Target]] 用於定向控制轉移(與本文無關)。

每一個抽象操做都隱式返回完成記錄。即便看起來抽象操做會返回一個簡單的類型(例如 Boolean),它也將被隱式包裝爲類型爲 normal 的完成記錄(請參見隱式完成值

注1:規格在這方面並不徹底一致;有些輔助函數返回裸值,而且其返回值按原樣使用,而無需從「完成記錄」中提取值。一般從上下文中能夠清楚地看出這一點。

注2:規範制定人員正在研究使「完成記錄」的處理更加明確。

若是算法拋出異常,則意味着返回帶有 [[Type]] throw 的完成記錄,而 [[Value]] 是異常對象。如今,咱們將忽略 breakcontinuereturn 類型。

ReturnIfAbrupt(argument) 意味着採起如下步驟:

  1. 若是 argument 忽然出現,則返回 argument
  2. argument 設置爲 argument.[[Value]]

也就是說,咱們檢查完成記錄;若是是忽然完成,會當即返回。不然從完成記錄中提取值。

ReturnIfAbrupt 可能看起來像一個函數調用,但事實並不是如此。它是致使返回 ReturnIfAbrupt() 的函數返回的緣由,而不是返回 ReturnIfAbrupt 函數自己的函數。它的行爲更像是 C 語言中的宏。

ReturnIfAbrupt 能夠這樣使用:

  1. objFoo()。 (obj 是 Completion Record。)
  2. ReturnIfAbrupt(obj)
  3. Bar(obj). (若是仍在此處,則 obj 是從 Completion Record 中提取的值)

如今問號開始起做用:? Foo() 等同於 ReturnIfAbrupt(Foo())。使用簡寫很實用:咱們沒必要每次都明確編寫錯誤處理代碼。

一樣, Let val be ! Foo() 等效於:

  1. 使 valFoo()
  2. 斷言: val 不是忽然完成的
  3. val 設置爲 val.[[Value]]

利用這些知識,咱們能夠這樣重寫 Object.prototype.hasOwnProperty

Object.prototype.hasOwnProperty(P)

  1. 使 PToPropertyKey(V)
  2. 若是 P 是忽然完成的,則返回 P
  3. P 設置爲 P.[[Value]]
  4. OToObject(this value)
  5. 若是 O 是忽然完成的,則返回 O
  6. O 設置爲 O.[[Value]]
  7. 使 tempHasOwnProperty(O, P)
  8. 若是temp是忽然完成的,則返回temp
  9. 使 temptemp.[[Value]]
  10. 返回 NormalCompletion(temp)

…咱們能夠這樣重寫 HasOwnProperty

HasOwnProperty(O, P)

  1. 斷言: Type(O)Object
  2. 斷言: IsPropertyKey(P)true.
  3. descO.[[GetOwnProperty]](P)
  4. 若是 desc 是忽然完成,則返回 desc
  5. desc 設置爲 desc.[[Value]]
  6. 若是 descundefined, 則返回 NormalCompletion(false)
  7. 返回 NormalCompletion(true)

咱們也能夠重寫不帶感嘆號的 [[GetOwnProperty]] 內部方法:

O.[[GetOwnProperty]]

  1. tempOrdinaryGetOwnProperty(O, P)
  2. 斷言: temp 不是忽然完成
  3. 使 temptemp.[[Value]]
  4. 返回 NormalCompletion(temp)

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

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

Return ? Foo()

規範使用 Return ? Foo() ——爲何有問號?

Return ? Foo() 擴展爲:

  1. 使 tempFoo()
  2. 若是temp是忽然完成的,則返回temp
  3. temp 設置爲 temp.[[Value]]
  4. 返回 NormalCompletion(temp)

Return Foo() 相同;對於忽然和正常完成,其行爲方式相同。

Return ? Foo() 僅出於編輯緣由而使用,以使其可以更明確地表達 Foo 返回完成記錄。

斷言

規範中的主張斷言了算法的不變條件。爲了清楚起見,添加了它們,但沒有對實現添加任何要求——實現中不須要檢查它們。

繼續

咱們已經創建了閱讀規範所需的知識,如 Object.prototype.hasOwnProperty 之類的簡單方法和諸如 HasOwnProperty 之類的抽象操做。它們仍然委託其餘抽象操做,可是基於本文,咱們應該可以弄清楚它們的做用。咱們將遇到屬性描述符,這是另外一種規範類型。

從 Object.prototype.hasOwnProperty 開始的函數調用圖

有用的連接

如何閱讀 ECMAScript 規範(https://timothygu.me/es-howto/):該教程從一個稍微不一樣的角度涵蓋了本文中的許多內容。


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索