我是阿傑,我們在 Day 4 介紹了 ECMAScript 中的 Abstraction Operations、List、Internal Methods、Internal Slots 等 4 個名詞,今天繼續聊聊以下另外 4 個:
?
& !
放心,這 4 個講完就結束了,真的...
Record 是 ECMAScript 內部的一種規範類型 (specification type),它看起來就像 JavaScript 的物件,但又不是...
為什麼這麼說?因為它少了很多 JavaScript 物件該有的特性 (例如原型鏈)!我們可以試著把它抽象化一些,應該就會比較好理解 - 可以把 Record 想像成是一個純粹的資料集合體 (aggregation),就這樣,沒有其他特別的!而這個集合體的每筆資料都是一個 name field , 以一對 key-value 的形式呈現,會像這樣:
{ [[Fild1]]:42, [[Field2]]: false, [[Field3]]: empty }
上面便定義了一筆有 3 個 field 的 Record,這些 field 是無序的,因為它並不是靠索引來取值的,而它也沒有像 JavaScript 物件那樣有原型鏈的繼承關係,它只是單純地列出了擁有哪些 field,換句話說,就是一個簡單的資料集合。
除了 JavaScript 用物件的形式來實現 Record, 常見的還有資料表 (data table) 的呈現方式,例如下面這張表格 (圖 5.1):
Note:
這種[[ ]]
雙括號的表示法不是 Record field 的專利,在 ECMAScript 中,Internal Method 和 Internal Slot 也是使用此種寫法
Completion Record 是 ECMAScript 專用的規範類型,它是一種特殊的 Record,它可以用來解釋在運行期間 (runtime) ,值的傳遞及流程控制 (control flow)的轉移,可以看作是流程執行到特定步驟的一個運行結果;換句話說,就是在規範中的每個步驟執行完都會明確地或隱性地回傳一個結果 - Completion Record。
Completion Record 包含 3 個 field,如下圖所示 (圖 5.1):
[[Type]]
- 即當下產生的 Completion Record 類型, 它的值為 normal、break、continue、retrun 跟 throw 5 選 1,但我們目前專注在 normal 跟 throw 即可。[[Value]]
- 當下產生的值會被記錄在這裡,可以是除了 Competion Record 以外的任何值 - 我們主要會看到正常生成的值、拋出異常的值或者為空。[[Target]]
- 表示控制流程要轉移到的目標,這邊可以先暫時略過。當 Completion Record 的 [[Type]]
為 normal 時,代表它是一個 normal completion,否則,一律是一個 abrupt completion。
要讀取一個 Completion Record 的值,前提它必須是一個 normal completion,不能是一個 abrupt completion。
這邊必須記住一件非常重要的事 - 就是一個可被呼叫的物件 (callable object) 永遠只會回傳 normal completion 或 throw completion 這兩者之一,這也是為什麼我們目前只需專注在它們兩個身上即可。
我們先來看一下它常出沒的地方 - 一個 abstrac opertaion 裡 (圖 5.3):
這個 ToObject abstract operation 明確地告訴你它會回傳一個包含 Object 的 normal completion 或一個異常的 throw completion。
為什麼要定義這個東西啊,是不是搞得太複雜了???這點我也很疑惑,直到我看到了 ECMAScript 閱讀指南 跟 How to Read the ECMAScript 的解釋,才總算有點理解,他們都不約而同的提到了一個東西 - try-catch block。
沒錯,ECMASript 裡並沒有 try-catch block 的存在,所以我們必須明確地處理每個可能出現的異常,也就是讓 [[Value]]
也同時擁有攜帶異常的能力;因為規範中使用了非常多的邏輯調用,如果沒有專門來處理異常的機制,那將會變得非常可怕,有興趣了解可以看看這兩篇文章的解釋!
ECMAScript 裡使用了大量的 Abstract Operation,目的是讓整份規範看起來更乾淨!
當 Abstract Operation 執行完畢會回傳一個 Completion Record,如果這個 Completion Record 是一個 normal completion 就直接將它的 [[Value]]
取出,如果是一個 abrupt completion,便直接回傳這個 completion。
而 ReturnIfAbrupt 的出現就是要簡化上述的這一系列步驟 (圖 5.4):
而且因為 Abstact Opertaion 是一個 callable 的物件,所以它只會回傳 normal completion 或 throw completion。
這也就表示,ReturnIfAbrupt(AbstractOperation())
的結果會是一個從 completion record 取出的值,不然就會回傳一個 throw completion。
如果你有稍微翻閱過 ECMAScript ,你會發現 ReturnIfAbrupt 出現的頻率其實很低,取而代之的是一套更精簡的寫法 - ?
跟 !
。
?
& !
我們可以將 ReturnIfAbrupt(OperationName)
直接簡寫成 ? OperationName()
,也就是在 abstract operation 前面加上 ?
這個前綴,我們先來看看它出現在規範裡的樣子:
上面這個 ? ToObject(this value)
最終可能會成為一個值或回傳一個 throw completion,也就表示這個 O 如果沒有拿到一個值,就會出現了一個異常情況 (exception)。
那有沒有操作是不會丟出異常的?答案是肯定的,所以也就有了 !
的出現,它代表這個操作永遠不會回傳 abrupt completion,我們來看看它出現在規範的哪 (圖 5.6):
上面這個 ! ToString(F(k))
一定會是一個從 normal completion 的 [[Value]]
field 取出來的值,永遠不會回傳 throw completion,但如果你點進這個 ToString(F(k))
abstract operation 觀看,你會發現它是有機會 throw exception 的,那為什麼這邊又使用了 !
呢?我想答案會在它的參數 F(k)
上。
我的老天鵝,辛苦大家看到這邊了...
大家如果還是很迷惑也不要擔心,我們接下來會直接從 Array method 來看 ECMAScript 怎麼寫,屆時就會看到想吐了,一定會熟悉的,沒事兒沒事兒!
希望大家可以開心地使用各種咩色,體驗它帶給你的便利,祝大家歸剛沒煩惱。