10分鐘搞懂toString和valueOf函數(詳細版)

  • 2019 年 10 月 4 日
  • 筆記

首先要說明的是這兩種方法是toPrimitive抽象操作里會經常用到的。

默認情況下,執行這個抽象操作時會先執行valueOf方法,如果返回的不是原始值,會繼續執行toString方法,如果返回的還不是原始值,那麼會報錯,如果有指定轉換類型時,情況又會有所不同,詳細解析請繼續往下看。

(注意:valueOf和toString方法在Date,array等對象中有些是被重寫過的,所以不同對象調用此方法可能產生的操作不同,如果沒有這些方法,會調用最原始的Object.prototype上的valueOf和toString方法)

數據的轉換

所有對象繼承了兩個轉換方法:

第一個是toString(),它的作用是返回一個反映這個對象的字元串

第二個是valueOf(),它的作用是返回它相應的原始值

toString()

toString()可以看做是把一個數據轉換成了相應字元串的形式,安照這個轉換規則中

案例:  //返回相應的字元串  console.log(    ({x:1,      y:1     }).toString()      );  // [object Object]  console.log([1,2,3].toString()); // 1,2,3  console.log((function(x){f(x); }).toString()); //function (x){f(x); }  console.log(/d+/g.toString()); // /d+/g  console.log(new Date(2015,4,4).toString()); // Mon May 04 2015 00:00:00 GMT+0800    console.log(new Date(2015,4,4).valueOf()); //  1430668800000

valueOf()

每個JavaScript固有對象的 valueOf 方法定義不同。

對象

返回值

Array

數組沒有valueOf方法,繼承的是Object.prototype.valueOf的原始方法,返回數組本身

Boolean

Boolean 值。

Date

存儲的時間是從 1970 年 1 月 1 日午夜開始計的毫秒數 UTC。

Function

同Array

Number

數字值。

Object

對象本身。這是默認情況。

String

字元串值。


Array,Function,Math 和 Error 對象沒有 valueOf 方法。

一般來說,對象到字元串的轉換經過了如下步驟:

1.如果對象具有toString()方法,則調用這個方法。如果它返回一個原始值,js將這個值轉換成字元串,並返還這個字元串結果。

2.如果對象沒有toString()方法,或者這個方法並不返回一個原始值,那麼js將調用valueOf()方法。

3.否則,js無法從toString()或者valueOf()獲得一個原始值,因此這時它將拋出一個類型錯誤異常。

一般來說,對象到數字的轉換過程中,js做了同樣類似的事情,但這裡它會首先嘗試使用valueOf()方法:

1.如果對象具有valueOf()方法,後者返回一個原始值,則js將這個原始值轉換成數字,並返回這個數字。

2.否則,如果對象具有toString()方法,後者返回一個原始值,則js將轉換並返回。

(首先js轉換成相應的字元串原始值,再繼續將這個原始值轉換成相應的數字類型,再返回數字)

3.否則,js拋出一個類型錯誤異常。

對象通過toString或valueOf方法轉換為原始值,JS語言核心的內置類首先嘗試使用valueOf(),再嘗試使用toString()

一個小李子

「1」 == true;

將返回true,轉換形式是:true首先轉換為1,然後再執行比較。接下來字元串「1」也轉換成了數字1,相等,所以返回true

另外如:

var str = new String('hello,world');  console.log(typeof str); //'object'  console.log(typeof str.valueOf()); //'string'

對於所有非日期對象來說,對象到原始值的轉換基本上是對象到數字的轉換

(首先調用valueOf,但日期對象則使用對象到字元串的轉換模式,但這種轉換隻執行一次就立即被使用,不會像上面所說的那般 先轉成字元串再轉成相應的數字類型)

比如說,js中「+"運算符可以進行數學加法和字元串連接操作。

如果他它的其中一個操作數是對象,則js將使用特殊的方法將對象轉換成原始值,而不是使用其他算術運算符的方法執行對象到數字的轉換,」==「運算符類似

和」==「一樣,」<"與其他運算符也會做對象到原始值的轉換,但要出去日期對象的特殊情形

「-「減號運算符把兩個操作數都轉換成數字

比如:

var now = new Date();  console.log(now);  // Date {Sat Apr 04 2015 13:21:08 GMT+0800}  console.log(typeof (now+1));  //string  console.log((now+1));  //Sat Apr 04 2015 13:21:08 GMT+08001  console.log(typeof (now-1));  //number  console.log((now-1));  // 1428124868474  console.log(now == now.toString());  //true  console.log(now > now -1); //true
var date = new Date();  var date_string = date.toString();  var date_value = date.valueOf();  alert(date == date_string); //true  alert(date == date_value); //false
var aaa = {  i: 10,  valueOf: function() { return this.i+30; },  toString: function() { return this.valueOf()+10; }  }  alert(aaa > 20); // true  alert(+aaa); // 40  alert(aaa); // 50 

之所以有這樣的結果,因為它們偷偷地調用valueOf或toString方法。更進一步測試

var bbb = {  i: 10,  toString: function() {  console.log('toString');  return this.i;  },  valueOf: function() {  console.log('valueOf');  return this.i;  }  }  alert(bbb);// 10 toString  alert(+bbb); // 10 valueOf  alert(''+bbb); // 10 valueOf  alert(String(bbb)); // 10 toString  alert(Number(bbb)); // 10 valueOf  alert(bbb == '10'); // true valueOf  alert(bbb === '10'); // false

乍一看結果,大抵給人的感覺是,如果轉換為字元串時調用toString方法,如果是轉換為數值時則調用valueOf方法,但其中有兩個很不和諧。

一個是alert(''+bbb),字元串合拼應該是調用toString方法……另一個我們暫時可以理解為===操作符不進行隱式轉換,因此不調用它們。

為了追究真相,我們需要更嚴謹的實驗。

var aa = {  i: 10,  toString: function() {  console.log('toString');  return this.i;  }  }  alert(aa);// 10 toString  alert(+aa); // 10 toString  alert(''+aa); // 10 toString  alert(String(aa)); // 10 toString  alert(Number(aa)); // 10 toString  alert(aa == '10'); // true toString  再看valueOf。  var bb = {  i: 10,  valueOf: function() {  console.log('valueOf');  return this.i;  }  }  alert(bb);// [object Object]  alert(+bb); // 10 valueOf  alert(''+bb); // 10 valueOf  alert(String(bb)); // [object Object]  alert(Number(bb)); // 10 valueOf  alert(bb == '10'); // true valueOf  發現有點不同吧?!它沒有像上面toString那樣統一規整。對於那個[object Object],我估計是從Object那裡繼承過來的,我們再去掉它看看。  Object.prototype.toString = null;  var cc = {  i: 10,  valueOf: function() {  console.log('valueOf');  return this.i;  }  }  alert(cc);// 10 valueOf  alert(+cc); // 10 valueOf  alert(''+cc); // 10 valueOf  alert(String(cc)); // 10 valueOf  alert(Number(cc)); // 10 valueOf  alert(cc == '10'); // true valueOf 

總結起來就是 如果只重寫了toString,對象轉換時會無視valueOf的存在來進行轉換。

但是,如果只重寫了valueOf方法,在要轉換為字元串的時候會優先考慮valueOf方法。在不能調用toString的情況下,只能讓valueOf上陣了