摘要:本文將介紹規范中的抽象操作。它們主要用于規范的說明,不需要被真正地實現。該抽象操作接受一個參數和一個可選的參數。根據規范中的加法操作,對于操作,會調用和把和轉化為原始數據類型。
本文將介紹ECMAScript7規范中的ToPrimitive抽象操作。
預備知識 ECMAScript數據類型ECMAScript數據類型細分為兩大類數據類型,一種是語言類型,一種是規范類型:
語言類型是可以直接被開發人員使用的數據類型;
規范類型代表meta-values(元值),用在算法中描述ECMAScript語言結構和語言類型的語義。它們主要用于規范的說明,不需要被真正地實現。
ECMAScript的語言類型一共有7種:
Undefined
Null
Boolean,布爾類型
String,字符串類型
Symbol,符號類型
Number,數字類型
Object,對象類型
原始數據類型是上述Undefined、Null、Boolean、String、Symbol和Number的統稱,也就是非對象數據類型。
下文涉及到的規范類型只有List,也就是列表,類似于數組,用符號? ?表示。
Symbol有很多有名的符號,比如@@toPrimitive,也就是Symbol.toPrimitive,這是定義在Symbol對象上的一個屬性。
ToPrimitive(input [, PreferredType])該抽象操作接受一個參數input和一個可選的參數PreferredType。該抽象操作的目的是把參數input轉化為非對象數據類型,也就是原始數據類型。如果input可以同時轉化為多個原始數據,那么會優先參考PreferredType的值。轉化過程參照下表:
參數input的數據類型 | 結果 |
---|---|
Undefined | 返回input自身 |
Null | 返回input自身 |
Boolean | 返回input自身 |
Number | 返回input自身 |
String | 返回input自身 |
Symbol | 返回input自身 |
Object | 執行下面的步驟 |
如果input的數據類型是對象,執行下述步驟:
如果沒有傳入PreferredType參數,讓hint等于"default";
如果PreferredType是hint String,讓hint等于"string";
如果PreferredType是hint Number,讓hint等于"number";
讓exoticToPrim等于GetMethod(input, @@toPrimitive),大概語義就是獲取參數input的@@toPrimitive方法;
如果exoticToPrim不是Undefined,那么:
讓result等于Call(exoticToPrim, input, ? hint ?),大概語義就是執行exoticToPrim(hint);
如果result是原始數據類型,返回result;
拋出類型錯誤的異常;
如果hint是"default",讓hint等于"number";
返回OrdinaryToPrimitive(input, hint)抽象操作的結果。
OrdinaryToPrimitive(O, hint)O的數據類型是對象,hint的數據類型是字符串,并且hint的值要么是"string",要么是"number"。該抽象操作的步驟如下:
如果hint是"string",讓methodNames等于? "toString", "valueOf" ?;
如果hint是"number",讓methodNames等于? "valueOf", "toString" ?;
按順序迭代列表methodNames,對于每一個迭代值name:
讓method等于Get(O, name),大概語義就是獲取對象O的name值對應的屬性;
如果method可以調用,那么:
讓method等于Call(method, O),大概語義就是執行method();
如果result的類型不是對象,返回result;
拋出類型錯誤的異常。
由上述操作步驟可知:
通過ToPrimitive的步驟6可知,當沒有提供可選參數PreferredType的時候,hint會默認為"number";
通過ToPrimitive的步驟4可知,可以通過定義@@toPrimitive方法來覆蓋默認行為,比如規范中定義的Date日期對象和Symbol符號對象都在原型上定義了@@toPrimitive方法。
實踐可能有人會問,為什么要講解規范中的抽象方法,抽象方法我又用不到。其實不然,這個方法在很多地方都會用到,只是你不知道罷了。下面通過講解幾個實例讓大家加深對它的理解。
"" + [1, 2, 3]"" + [1, 2, 3] // "1,2,3"
根據規范中的加法操作,對于操作x + y,會調用ToPrimitive(x)和ToPrimitive(y)把x和y轉化為原始數據類型。上面的例子中""本身就是原始數據類型了,所以返回""自身。[1, 2, 3]是對象類型,并且數組沒有定義@@toPrimitive屬性。因為沒有提供PreferredType,所以在ToPrimitive操作的步驟6中,hint變為"number",所以OrdinaryToPrimitive中的methodNames是? "valueOf", "toString" ?。
var a = [1, 2, 3] a.valueOf() // [1, 2, 3],數組a本身 a.toString() // "1,2,3"
因為valueOf返回的是數組a本身,還是對象類型,所以會繼續調用toString方法,返回了字符串"1,2,3",所以
"" + [1, 2, 3] // => "" + "1,2,3" => "1,2,3"
那么,如果我們覆蓋數組原型上的valueOf方法,使得該方法返回一個原始數據類型,那么結果會是什么呢?
var a = [1, 2, 3] a.valueOf = function () { console.log("trigger valueOf") return "hello" } "" + a // => "" + "hello" => "hello"
覆蓋默認的valueOf之后,調用valueOf會返回原始數據類型。根據OrdinaryToPrimitive的3.2.2,這個時候就直接返回了,不會再調用toString方法。同時在控制臺會log出"trigger valueOf",也就是說valueOf確實是調用了。
那么,如果我們覆蓋數組默認的toString方法,使得該方法返回對象類型,那么結果會是什么呢?
var a = [1, 2, 3] a.toString = function () { console.log("trigger toString") return this } "" + a // Uncaught TypeError: Cannot convert object to primitive value
因為數組原型上的valueOf方法返回對象類型,在上面的例子中,我們把toString覆蓋了,使它也返回對象類型,那么就會直接走到OrdinaryToPrimitive的第4步,也就是拋出類型錯誤的異常,不能把對象轉化為原始數據類型。
在上面我們提到過可以通過@@toPrimitive方法來自定義ToPrimitive的行為,比如下面的例子:
var a = [1, 2, 3] a[Symbol.toPrimitive] = function () { return "custom" } "" + a // => "" + "custom" => "custom"
相加操作在調用ToPrimitive的時候沒有提供PreferredType,接下來講一個會優先使用hint String作為PreferredType的例子:
var a = [1, 2, 3] a.valueOf = function () { console.log("trigger valueOf") return "hello" } a.valueOf() // "hello" a.toString() // "1,2,3" var obj = {} obj[a] = "hello" // obj是{1,2,3: "hello"}
在把變量作為鍵值使用的時候,會調用ToPrimitive把鍵值轉化為原始數據類型,并且PreferredType的值是hint String。通過上面的例子也可以看出來,a.valueOf和a.toString的結果都是字符串,但是使用了"1,2,3",也就是使用了a.toString的結果。當然,如果我們重新定義toString方法,并且返回對象,那么就會使用valueOf的值了:
var a = [1, 2, 3] a.valueOf = function () { console.log("trigger valueOf") return "hello" } a.toString = function () { console.log("trigger toString") return this } var obj = {} obj[a] = "hello" // obj是{hello: "hello"}
并且會在控制臺先log出"trigger toString",后log出"trigger valueOf"。當然,如果這兩個都返回對象,那么還是會報錯:
var a = [1, 2, 3] // 使用原型鏈上的valueOf方法 a.toString = function () { console.log("trigger toString") return this } var obj = {} obj[a] = "hello" // Uncaught TypeError: Cannot convert object to primitive valueDate
在上面講ToPrimitive的時候,提到Date對象和Symbol對象在原型上定義了@@toPrimitive方法。在ToPrimitive的第6步的操作中,我們可以看到當沒有提供PreferredType的時候,優先調用valueOf方法。Date原型上的@@toPrimitive做的事情非常簡單:當沒有提供PreferredType的時候,優先調用toString方法。所以對于上面的操作,Date對象的行為是不一樣的:
var a = [1, 2, 3] a.valueOf = function () { return "hello" } a.valueOf() // "hello" a.toString() // "1,2,3" "" + a // "hello" var date = new Date() date.valueOf() // 1536416960724 date.toString() // "Sat Sep 08 2018 22:29:20 GMT+0800 (中國標準時間)" "" + date // "Sat Sep 08 2018 22:29:20 GMT+0800 (中國標準時間)"
我們可以看到date的valueOf方法和toString方法都返回原始數據類型,但是優先使用了toString方法。
總結本文主要講解了ToPrimitive抽象操作,以及一些相關的例子,希望大家能有所收獲。如果本文有什么錯誤或者不嚴謹的地方,歡迎在評論區留言。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/97583.html
概述 本文主要講解JavaScript中的三種相等運算:==,===和Object.is()。通過對比和例子,加深大家的印象,并就個別例子進行詳細說明。 預備知識 ECMAScript7規范中的ToPrimitive抽象操作 ===運算符 對于x === y,該運算符的比較步驟如下: 如果x的類型和y的類型不一樣,返回false; 如果x的類型是數字,那么: 如果x是NaN,返回false;...
摘要:本文將介紹一段使用隱式類型轉換輸出的代碼,并講解具體的轉換過程。代碼轉換過程我們分四部分講解具體的轉換過程,一個空數組,緊跟在數組后面的的語義應該是表示屬性操作,類似于中的作用,而不是表示數組。 本文將介紹一段使用JavaScript隱式類型轉換輸出nb的代碼,并講解具體的轉換過程。 預備知識 請先閱讀文章ECMAScript7規范中的ToPrimitive抽象操作。 代碼 ([][[...
摘要:本文主要講解規范中的操作符。由上述步驟可知,如果是一個函數,那么會重新在綁定的目標函數上執行操作。而使用的方式的時候,給構造函數添加一個靜態方法,相當于給對象賦值,賦值操作會先檢查原型鏈上是否存在同名屬性,所以就會有賦值失敗的風險。 本文主要講解ECMAScript7規范中的instanceof操作符。 預備知識 有名的Symbols 有名的Symbols指的是內置的符號,它們定義在S...
摘要:為了避免某些場景下的意外,甚至推崇直接使用來代替。使用了運算符的一些規則,發生了類型轉換。按照以下規則轉換被傳遞參數直接返回直接返回直接返回直接返回直接返回返回一個對象的默認值。 前言 類型轉換在各個語言中都存在,而在 JavaScript 中由于缺乏對其的了解而不慎在使用中經常造成bug被人詬病。為了避免某些場景下的意外,甚至推崇直接使用 Strict Equality( === )...
摘要:另一方面,我不建議初次接觸的開發人員閱讀規范。在維護語言的最新規范。在這一點上,我想指出的是,絕對沒有人從上到下閱讀規范。拓展閱讀由于的定義,中的細節如冒泡錯誤,直到塊在規范中不存在。換句話說,會轉發中拋出的錯誤,并終止其余的步驟。 翻譯自:How to Read the ECMAScript Specification Ecmascript 語言規范 The ECMAScr...
閱讀 850·2021-11-16 11:56
閱讀 1673·2021-11-16 11:45
閱讀 3120·2021-10-08 10:13
閱讀 4110·2021-09-22 15:27
閱讀 733·2019-08-30 11:03
閱讀 651·2019-08-30 10:56
閱讀 953·2019-08-29 15:18
閱讀 1747·2019-08-29 14:05