聊一聊valueOf和toString

valueOf和toString是Object.prototype的方法。通常不多直接調用,可是在使用對象參與運算的時候就會調用這兩個方法了。我想大部分人都存在如下疑問:javascript

  • valueOf和toString哪一個優先級較高?
  • 是否是全部場景都會調用valueOf和toString

概念解釋

  • valueOf: 返回對象的原始值表示
  • toString: 返回對象的字符串表示

在介紹下面的內容以前先了解一下轉換規則,下面的內容解釋都是基於這個規則表來的:java

valueOf轉換規則

valueOf是Object.prototype的方法,由Object來的對象都會有該方法,可是不少內置對象會重寫這個方法,以適合實際須要。面試

說到原始值就必須說到原始類型,JS規範中 原始類型 以下:數組

  • Boolean
  • Null
  • Undefined
  • Number
  • String

非原始值(也就是對象)重寫規則以下:函數

對象 valueOf返回值
Array 數組自己
Boolean 布爾值
Date 返回毫秒形式的時間戳
Function 函數自己
Number 數字值
Object 對象自己
String 字符串值
如下規則是通過驗證的,若是對驗證過程不關心,能夠只看轉換規則。

建議看一下驗證過程,這樣能夠加深理解測試

對象轉換爲布爾值

  1. 直接轉換爲true(包裝類型也同樣),不調用valueOf和toString

對象轉換爲數字

在預期會將對象用做數字使用時,好比參與算術運算等等操做,對象轉換爲數字會依次調用valueOf和toString方法,具體規則以下:this

  1. 若是對象具備valueOf方法且返回原始值(string、number、boolean、undefined、null),則將該原始值轉換爲數字(轉換失敗會返回NaN),並返回這個數字
  2. 若是對象具備toString方法且返回原始值(string、number、boolean、undefined、null),則將該原始值轉換爲數字(轉換失敗會返回NaN),並返回這個數字
  3. 轉換失敗,拋出TypeError

對象轉換爲字符串

  1. 若是對象具備toString方法且返回原始值(string、number、boolean、undefined、null),則將該原始值轉換爲字符串,並返回該字符串
  2. 若是對象具備valueOf方法且返回原始值(string、number、boolean、undefined、null),則將該原始值轉換爲字符串,並返回該字符串
  3. 轉換失敗,拋出TypeError

toString轉換規則

對象 toString返回值
Array 以逗號分割的字符串,如[1,2]的toString返回值爲"1,2"
Boolean "True"
Date 可讀的時間字符串,如"Tue Oct 15 2019 12:20:56 GMT+0800 (中國標準時間)"
Function 聲明函數的JS源代碼字符串
Number "數字值"
Object "[object Object]"
String "字符串"

驗證對象到原始值的轉換

光看valueOf和toString沒啥東西可說,平常開發中也不多直接調用,可是當咱們將對象當作原始值來使用時會發生轉換,並且轉換過程還略微有點迷糊。spa

對象轉換爲Boolean

爲了可以直觀的看到JS內部的轉換過程,我把valueOf和toString都簡單重寫了,加了日誌。prototype

// 保存原始的valueOf
var valueOf = Object.prototype.valueOf;
var toString = Object.prototype.toString;

// 添加valueOf日誌
Object.prototype.valueOf = function () {
    console.log('valueOf');
    return valueOf.call(this);
};
// 添加toString日誌
Object.prototype.toString = function () {
    console.log('toString');
    return toString.call(this);
};
var a = {};
var b = new Boolean(false);

if (a) {
    console.log(1);
}
if(b) {
    console.log(2);
}

以上例子的輸出以下:日誌

1
2
未調用valueOf和toString,符合[對象到布爾值]的轉換規則

對象轉換爲Number

例子1

// 保存原始的valueOf
var valueOf = Object.prototype.valueOf;
var toString = Object.prototype.toString;

// 添加valueOf日誌
Object.prototype.valueOf = function() {
    console.log('valueOf');
    return valueOf.call(this);
};
// 添加toString日誌
Object.prototype.toString = function() {
    console.log('toString');
    return toString.call(this);
};
var a = {};
console.log(++a);

輸出以下:

valueOf
toString
NaN

分析

  1. valueOf方法返回的是對象自己,不是原始值,繼續執行
  2. toString方法返回的是"[object Object]",是原始值(字符串),將字符串轉換爲數字NaN

例子2

// 保存原始的valueOf
var valueOf = Object.prototype.valueOf;
var toString = Object.prototype.toString;

// 添加valueOf日誌
Object.prototype.valueOf = function () {
    console.log('valueOf');
    return "1"; // 強制返回原始值
};
// 添加toString日誌
Object.prototype.toString = function () {
    console.log('toString');
    return toString.call(this);
};
var a = {};
console.log(++a);

輸出以下:

valueOf
2

分析

  1. valueOf返回原始值(字符串),直接將該字符串轉換爲數字,獲得1

對象轉換爲字符串

在預期會將對象用做字符串時,好比一個字符串拼接了字符串,傳入了一個對象,此時會發生轉換。

// 保存原始的valueOf
var valueOf = Object.prototype.valueOf;
var toString = Object.prototype.toString;

// 添加valueOf日誌
Object.prototype.valueOf = function () {
    console.log('valueOf');
    return valueOf.call(this);
};
// 添加toString日誌
Object.prototype.toString = function () {
    console.log('toString');
    return toString.call(this);
};
var a = {};
alert(a);

輸出以下:

toString
// 彈出[object Object]

分析

  1. 調用toString方法,返回了字符串"[object Object]",對象最終轉換爲該字符串

例子2

// 保存原始的valueOf
var valueOf = Object.prototype.valueOf;
var toString = Object.prototype.toString;

// 添加valueOf日誌
Object.prototype.valueOf = function () {
    console.log('valueOf');
    return valueOf.call(this);
};
// 添加toString日誌
Object.prototype.toString = function () {
    console.log('toString');
    return this;
};
var a = {};
alert(a);

輸出以下:

toString
valueOf
Uncaught TypeError: Cannot convert object to primitive value
    at 1.js:16

分析

  1. 調用toString方法,返回的不是原始值,繼續執行
  2. 調用valueOf方法,返回的不是原始值,繼續執行
  3. 拋出TypeError

【番外】使用加號運算符鏈接字符串和對象時的處理

在測試對象到字符串轉換時發現以下代碼的表現和預期並不一致:

// 保存原始的valueOf
var valueOf = Object.prototype.valueOf;
var toString = Object.prototype.toString;

// 添加valueOf日誌
Object.prototype.valueOf = function () {
    console.log('valueOf');
    return valueOf.call(this);
};
// 添加toString日誌
Object.prototype.toString = function () {
    console.log('toString');
    return toString.call(this);
};

console.log("a" + {});

輸出以下:

valueOf
toString
a[object Object]

疑問

"a"+ {} 應該是預期把{}當作字符串使用,應該先調用toString方法的,實際狀況卻不是這樣。

結論

經過查找資料獲得的結論以下:

  1. 若是有一個是對象,則遵循對象對原始值的轉換過程(Date對象直接調用toString完成轉換,其餘對象經過valueOf轉化,若是轉換不成功則調用toString)
  2. 若是兩個都是對象,兩個對象都遵循步驟1轉換到字符串
  3. 兩個數字,進行算數運算
  4. 兩個字符串,直接拼接
  5. 一個字符串一個數字,直接拼接爲字符串

面試題

var a = {};
var b = {};
var c = {};
c[a] = 1;
c[b] = 2;

console.log(c[a]);
console.log(c[b]);

題解

因爲對象的key是字符串,因此c[a]c[b]中的ab會執行[對象到字符串]的轉換。

根據轉換規則, ab都轉換爲了[object Object],因此c[a]c[b]操做的是同一個鍵。

答案是輸出兩個2,c對象的最終結構以下:

{
  '[object Object]':2
}

0.jpeg

相關文章
相關標籤/搜索