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