本篇文章給大家?guī)砹薐avaScript中類型轉(zhuǎn)換的相關(guān)知識,將值轉(zhuǎn)換為原始值、轉(zhuǎn)換為數(shù)字和轉(zhuǎn)換為字符串,這剛好對應(yīng)了引擎內(nèi)部的三種抽象操作:ToPrimitive()、ToNumber()、ToString(),希望對大家有幫助。
JavaScript 加法規(guī)則
在JavaScript中,加法的規(guī)則其實很簡單,只有兩種情況:
-
數(shù)字和數(shù)字相加
-
字符串和字符串相加
所有其他類型的值都會被自動轉(zhuǎn)換成這兩種類型的值
在JavaScript中,一共有兩種類型的值:
-
原始值有:undefined、null、 布爾值(boolean)、 數(shù)字(number)、字符串(string)、symbol
-
對象值:其他的所有值都是對象類型的值,包括數(shù)組(arrays)和函數(shù)(functions)
類型轉(zhuǎn)換
加法運算符會觸發(fā)三種類型轉(zhuǎn)換:將值轉(zhuǎn)換為原始值、轉(zhuǎn)換為數(shù)字、轉(zhuǎn)換為字符串,這剛好對應(yīng)了JavaScript引擎內(nèi)部的三種抽象操作:ToPrimitive()、ToNumber()、ToString()
通過 ToPrimitive() 將值轉(zhuǎn)換為原始值
ToPrimitive(input, PreferredType?)
可選參數(shù)PreferredType可以是Number或者String,它只代表了一個轉(zhuǎn)換的偏好,轉(zhuǎn)換結(jié)果不一定必須是這個參數(shù)所指的類型,但轉(zhuǎn)換結(jié)果一定是一個原始值.如果PreferredType被標(biāo)志為Number,則會進(jìn)行下面的操作來轉(zhuǎn)換輸入的值 (§9.1):
-
如果輸入的值已經(jīng)是個原始值,則直接返回它.
-
否則,如果輸入的值是一個對象.則調(diào)用該對象的valueOf()方法.如果valueOf()方法的返回值是一個原始值,則返回這個原始值.
-
否則,調(diào)用這個對象的toString()方法.如果toString()方法的返回值是一個原始值,則返回這個原始值.
-
否則,拋出TypeError異常.
如果PreferredType被標(biāo)志為String,則轉(zhuǎn)換操作的第二步和第三步的順序會調(diào)換.如果沒有PreferredType這個參數(shù),則PreferredType的值會按照這樣的規(guī)則來自動設(shè)置: Date類型的對象會被設(shè)置為String,其它類型的值會被設(shè)置為Number.
通過ToNumber()將值轉(zhuǎn)換為數(shù)字
如果輸入的值是一個對象,則會首先會調(diào)用ToPrimitive(obj, Number)將該對象轉(zhuǎn)換為原始值,然后在調(diào)用ToNumber()將這個原始值轉(zhuǎn)換為數(shù)字.
通過ToString()將值轉(zhuǎn)換為字符串
如果輸入的值是一個對象,則會首先會調(diào)用ToPrimitive(obj, String)將該對象轉(zhuǎn)換為原始值,然后再調(diào)用ToString()將這個原始值轉(zhuǎn)換為字符串.
demo
var obj = { valueOf: function () { console.log("valueOf"); return {}; // 沒有返回原始值 }, toString: function () { console.log("toString"); return {}; // 沒有返回原始值 } }
Number作為一個函數(shù)被調(diào)用(而不是作為構(gòu)造函數(shù)調(diào)用)時,會在引擎內(nèi)部調(diào)用ToNumber()操作:
Number(obj) // output valueOf toString Uncaught TypeError: Cannot convert object to primitive value String(obj) // output toString valueOf Uncaught TypeError: Cannot convert object to primitive value
加法
value1 + value2
在計算這個表達(dá)式時,操作步驟是這樣的:
-
將兩個操作數(shù)轉(zhuǎn)換為原始值 (下面是數(shù)學(xué)表示法,不是JavaScript代碼):
prim1 := ToPrimitive(value1) prim2 := ToPrimitive(value2)
PreferredType被省略,因此 Date 類型的值采用String,其他類型的值采用Number.
-
如果 prim1 或者 prim2 中的任意一個為字符串,則將另外一個也轉(zhuǎn)換成字符串,然后返回兩個字符串連接操作后的結(jié)果;
-
否則,將 prim1 和 prim2 都轉(zhuǎn)換為數(shù)字類型,返回他們的和。
[]+[]
輸出: ''
[]會被轉(zhuǎn)換成一個原始值,首先嘗試 valueOf() 方法,返回數(shù)組本身(this):
> var arr = []; > arr.valueOf() === arr true
這樣的結(jié)果不是原始值,所以再調(diào)用 toString() 方法,返回一個空字符串(是一個原始值)。因此,[] + [] 的結(jié)果實際上是兩個空字符串的連接.
> [] + {} '[object Object]'
{} + {}
輸出:NaN
-
JavaScript引擎將第一個{}解釋成了一個空的代碼塊并忽略了它
-
這里的加號并不是代表加法的二元運算符,而是一個一元運算符,作用是將它后面的操作數(shù)轉(zhuǎn)換成數(shù)字,和 Number() 函數(shù)完全一樣。例如:
+{} Number({}) Number({}.toString()) // 因為{}.valueOf()不是原始值 Number("[object Object]") NaN
> {} + [] 0
-
{} 忽略
-
+[] = Number([]) = Number([].toString()) = Number("") = 0
有趣的是,Node.js的REPL在解析類似的輸入時,與Firefox和Chrome(和Node.js一樣使用V8引擎)的解析結(jié)果不同.下面的輸入會被解析成一個表達(dá)式,結(jié)果更符合我們的預(yù)料:
> {} + {} '[object Object][object Object]' > {} + [] '[object Object]'
總結(jié)
對象.valueOf() === 對象
數(shù)組對象.toString() === 數(shù)組對象.join()
對象.toString() === "[object Object]"
Javacript 中 + 號工作:
-
數(shù)字 + 數(shù)字
-
字符串 + 字符串
【