淺析JavaScript對象轉換成原始值
前言
首先拋出幾個問題:
console.log(Boolean({})); console.log(Number([])); console.log(Number([6])); console.log(([] + []).length); console.log(({} + {}).length);
打印結果都是啥?不知道各位能答對幾題。文章末尾,我們會揭曉答案。這篇文章建議大傢耐心閱讀,相對來說比較繞而且乏味 – -。
三種算法
JavaScript 將對象轉換為原始值時遵循的算法規則比較復雜,這些規則冗長、晦澀,我們先簡單瞭解一下,具體實現後面再詳細展開。
對象轉換成原始值有三種基本算法:
- 偏字符串算法。該算法返回原始值,而且隻要可能就返回字符串。
- 偏數值算法。該算法返回原始值,而且隻要可能就返回數值。
- 無偏好算法。該算法不傾向於任何原始值類型,而是由類定義自己的轉換規則。JavaScript 內置類除瞭 Date 類,其他都實現瞭偏數值算法。Date 類實現瞭偏字符串算法。
對象轉換成佈爾值
對象到佈爾值的轉換最簡單:所有對象都轉換為true
。這個轉換不需要使用前面介紹的轉換算法,直接適用於所有對象,包括空數組、包裝對象:
console.log(Boolean([])); // => true const wrapperObject = new Boolean(false); // 這是一個對象,而不是原始值 console.log(Boolean(wrapperObject)); // => true
對象轉換成字符串
在將對象轉換成字符串時,首先使用偏字符串算法將它轉換為一個原始值,然後將得到的原始值再轉換為字符串。
這種轉換會發生在把對象傳給一個接收字符串參數的內置函數時,比如將String()
作為轉換函數,或者將對象插入模板字面量中時就會發生這種轉換:
console.log(String({})); // => '[object Object]' console.log(String([])); // => '' console.log(String(function () {})); // => 'function () {}'
對象轉換成數值
當需要把對象轉換為數值時,首先使用偏數值算法將它轉換為一個原始值,然後將得到的原始值再轉換為數值。
接收數值參數的內置函數和方法都以這種方式將對象轉換為數值,除數值操作符之外的多數操作符也按照這種方式把對象轉換為數值:
console.log(Number([])); // => 0 console.log(Number({})); // => NaN console.log(+[]); // => 0
上面的栗子瞭解一下即可,具體的轉換算法後面會詳細解釋。
轉換時使用的方法
toString()
、valueOf()
,所有對象都會繼承這兩個在對象到原始值轉換時使用的方法,在接下來解釋偏字符串、偏數值、無偏好轉換算法之前,我們必須先瞭解這兩個方法。
toString()
toString()
的任務是返回對象的字符串表示。
默認情況下,toString()
方法會返回特殊值:
const obj = { x: 1, y: 2, } obj.toString(); // => '[object Object]'
但是很多類都定義瞭自己特有的toString()
版本。
比如,Array
類的toString()
方法會將數組的每個元素轉換為字符串,然後再使用逗號作為分隔符將它們拼接起來:
const array = [1, 2, 3]; array.toString(); // => '1', '2', '3'
Function
類的toString()
方法會將用戶定義的函數轉換為 JavaScript 源代碼的字符串:
const f = function () {}; f.toString() // => 'function () {}'
Date
類定義的toString()
方法返回一個對人類友好的日期和時間字符串。
RegExp
類定義的toString()
方法會將RegExp
對象轉換為一個看起來像RegExp
字面量的字符串。
valueOf()
valueOf()
方法的設計意圖是返回對象的原始值表示。
然而大多數對象都沒有缺省的原始值表示,為此valueOf()
的默認實現是返回對象本身,內置的函數、數組等類型都是如此:
const obj = {x: 666}; const arr = [1, 2, 3]; const fn = function () {}; obj.valueOf(); // => {x: 666} arr.valueOf(); // => [1, 2, 3] fn.valueOf(); // => ƒ () {}
極少數對象才具有有意義的原始值表示,比如String
、Number
、Boolean
這樣的包裝類定義的valueOf()
方法會簡單地返回被包裝的原始值:
const wrapperObj1 = new String(666); const wrapperObj2 = new Number(888); const wrapperObj3 = new Boolean(false); wrapperObj1.toString(); // => '666' wrapperObj2.toString(); // => '888' wrapperObj3.toString(); // => 'false'
還有Date
對象定義的valueOf()
方法返回時間戳:
const d = new Date(2022, 6, 24); d.valueOf(); // => 1658592000000
瞭解完toString()
和valueOf()
方法後,接下來我們看看轉換算法是如何實現的。
轉換算法
偏字符串算法
偏字符串算法首先嘗試toString()
方法。如果這個方法有定義且返回原始值,則使用這個原始值,即使這個值不是字符串。如果toString()
方法不存在,或者存在但返回對象,則嘗試valueOf()
方法。如果valueOf()
方法存在且返回原始值,就使用該值。否則,轉換失敗,報 TypeError。
偏數值算法
偏數值算法與偏字符串算法類似,隻不過是先嘗試valueOf()
方法,再嘗試toString()
方法。
無偏好算法
無偏好算法取決於被轉換的對象。如果是一個Date
對象,則使用偏字符串算法。如果是其他類型的對象,就使用偏數值算法。
以上規則適用於所有的內置 JavaScript 類型。
練習題
文章開頭列舉的五個問題,下面我們逐一揭曉答案。
console.log(Boolean({}))
:對象轉換成佈爾值時,所有對象都轉換為true
,所以打印true
。
console.log(Number([]))
:對象轉換成數值,首先使用偏數值算法把對象轉換為一個原始值,然後再把得到的原始值轉換為數值。偏數值算法會先嘗試valueOf()
,將toString()
作為備用。Array
類繼承瞭默認的valueOf()
方法,所以不會返回原始值,因此最終會調用toString()
方法。空數組會被轉換成空字符串,而空字符串轉換成數值為 0,所以打印 0。
console.log(Number([6]))
:同樣,使用偏數值算法,最終會調用toString()
方法。因為數組中隻包含一個數值,所以該數值首先會被轉換成字符串 '6',再轉換回數值 6,打印結果為 6。
console.log(([] + []).length)
:兩個數組相加,首先會將數組轉換為字符串,使用偏字符串算法,空數組會轉換為空字符串,空字符串相加還是空字符串,長度為 0,打印結果為 0。
console.log(({} + {}).length)
:兩個對象相加,首先會將對象轉換成字符串,使用偏字符串算法,對象轉換成字符串後是'[object Object]'
,兩個'[object Object]'
拼接後的長度是 30,打印結果為 30。
到此這篇關於淺析JavaScript對象轉換成原始值的文章就介紹到這瞭,更多相關JS對象轉換內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 分享JavaScript 類型判斷的幾種方法
- 一篇文章讓你輕松記住js的隱式轉化
- JavaScript數據類型轉換詳解(推薦)
- JavaScript原始值與包裝對象的詳細介紹
- JavaScript中如何判斷對象是否為空的方法