首頁 > web前端 > js教程 > 主體

詳解ECMAScript7規範中ToPrimitive抽像操作的知識(範例)

不言
發布: 2018-09-17 14:53:07
原創
4631 人瀏覽過

這篇文章帶給大家的內容是關於ECMAScript7規格中ToPrimitive抽像操作的詳細解析(範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

本文將介紹ECMAScript7規格中的ToPrimitive抽像操作。

預備知識

ECMAScript資料型別

ECMAScript資料型別細分為兩大類別資料型別,一種是語言類型,一種是規範類型:

語言類型是可以直接被開發人員使用的資料型別;

規範型別代表meta-values(元值),用在演算法中描述ECMAScript語言結構和語言類型的語意。它們主要用於規範的說明,不需要被真正實現。

ECMAScript的語言類型一共有7種:

Undefined

#Null

Boolean,布林類型

##String,字串類型

Symbol,符號類型

Number,數字類型

Object,物件類型

原始資料類型是上述Undefined、Null、Boolean、String、Symbol和Number的統稱,也就是非物件資料型態。

下文涉及的規範類型只有List,也就是列表,類似數組,用符號« »表示。

@@toPrimitive

Symbol有許多有名的符號,像是@@toPrimitive,也就是Symbol.toPrimitive,這是定義在Symbol物件上的一個屬性。

ToPrimitive(input [, PreferredType])

此抽象運算接受一個參數input和一個可選的參數PreferredType。這個抽像操作的目的是把參數input轉換為非物件資料類型,也就是原始資料類型。如果input可以同時轉換為多個原始數據,那麼就會優先參考PreferredType的值。轉換過程參考下表:

參數#Undefined傳回input自身#Null 傳回input自身 #Boolean返回input自身Number#回傳input自身
input#的資料型別
####### ##返回input自身############Symbol######回傳input自身###########Object######執行下面的步驟############

如果input的資料型別是對象,執行下述步驟:

#1、如果沒有傳入PreferredType參數,讓hint等於"default";

2、如果PreferredType是hint String,讓hint等於"string";

3、如果PreferredType是hint Number,讓hint等於"number";

4、讓exoticToPrim等於GetMethod(input, @@toPrimitive),大概語意就是取得參數input的@@toPrimitive方法;

5、如果exoticToPrim不是Undefined,那麼:

讓result等於Call(exoticToPrim, input, « hint »),大概語意就是執行exoticToPrim(hint);

如果result是原始資料類型,則傳回result;

拋出類型錯誤的例外;

6、如果hint是"default",讓hint等於"number";

7、傳回OrdinaryToPrimitive(input, hint)抽象運算的結果。

OrdinaryToPrimitive(O, hint)

O的資料型別是對象,hint的資料型別是字串,而hint的值不是"string",就是"number"。這個抽象運算的步驟如下:

1、如果hint是"string",讓methodNames等於« "toString", "valueOf" »;

2、如果hint是"number",讓methodNames等於« "valueOf", "toString" »;

3、依序迭代列表methodNames,對於每一個迭代值name:

讓method等於Call(method, O),大概語意就是執行method();

如果result的型別不是對象,回傳result;

讓method等於Get(O, name),大概語意就是取得對象O的name值對應的屬性;

如果method可以調用,那麼:

4、拋出類型錯誤的例外。

由上述操作步驟可知:

透過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 value
登入後複製

Date

在上面讲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抽象操作,以及一些相关的例子,希望大家能有所收获。

以上是詳解ECMAScript7規範中ToPrimitive抽像操作的知識(範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板