天天看點

JavaScript的toString()和valueOf()差別到底是什麼

很多人一直分不清到底是什麼是tostring()和valueof(),更不知道他們之間有什麼差別,下面我們就一起來探讨一下這兩個函數。

從《JavaScript進階程式設計》這本書裡面

var colors = ["red", "blue", "green"]; // 建立一個包含3 個字元串的數組  
alert(colors.toString()); // red,blue,green  
alert(colors.valueOf()); // red,blue,green  
alert(colors); // red,blue,green 
           

的這個例子,我們會有一個非常大的疑問

toString()和valueOf()差別到底是什麼?

那麼不如看下一個我寫的例子

var arr = [,,];  
alert(Array.isArray(arr.valueOf()));  
alert(Array.isArray(arr.toString()));  
           

猜猜結果是怎麼樣的?

結果是第一個是true而第二個是false

為什麼呢,其實valueOf()調用完以後還是傳回一個數組

這個數組被alert的時候會調用toString()函數

是以不是valueOf()和toString()函數相同,而是,根本就是間接的調用了toString()函數!

什麼?你不信?

那好吧,我們來測試一下

var arr = [,,];  
arr.toString = function () {  
    alert("你調用了toString函數");  
}  
alert(arr.valueOf());  
           

來來,猜猜結果。

結果就是我們會看到“你調用了toString函數”。

而對于數值,我們可以調用valueOf的時候直接可以獲得數字進行計算,不必轉化成字元串,是以不會調用toString

反言之,如果我們需要獲得操作對象的字元串形式的時候就會調用其toString函數

舉個例子

以下代碼來自腳本之家的相關文章

var bbb = {  
 i: ,  
 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  
           

他的文章講的不是很清楚,為什麼在我們重寫了代碼以後就會出現這樣的情況

在這裡我說一下我的了解

valueOf的意思是傳回最适合該對象類型的原始值,而toString則是将在該對象類型的原始值以字元串形式傳回。

第一個

這裡我們的alert函數需要是的字元串,是以擷取的是字元串,而不是原始值,故而調用了toString

第二個

同理,alert要的是字元串不是原始值,其實是+bbb這個東西被調用了toString,而bbb被調用了valueOf

為了驗證我們這樣寫

var a = {  
    i: ,  
    valueOf: function () {  
        alert("你調用了a的valueOf函數");  
        return this.i;  
    },  
    toString: function () {  
        alert("你調用了a的toString函數");  
        return this.i;  
    }  
};  
var c = {  
    i: +a,  
    valueOf: function () {  
        alert("你調用了c的valueOf函數");  
        return this.i;  
    },  
    toString: function () {  
        alert("你調用了c的toString函數");  
        return this.i;  
    }  
};  
alert(c);  
           

其中讓c=+a,那麼即可知道結果,果然如此,調用了a的valueOf和c的toString

第三個

同理,我們可以把上一段我寫的程式裡面的c:+a改成c:’’+a

第四個

String這個強制轉換其實在其内部是調用了傳入參數的toString函數……

第五個

這個是有差別的,因為bbb的i屬性是數值類型的,如果i為11111xxxxx這樣的字元串,我們就可以看到調用了bbb的toString了

呐,代碼例子

var c = {  
    i: "11111xxxx",  
    valueOf: function () {  
        alert("你調用了c的valueOf函數");  
        return this.i;  
    },  
    toString: function () {  
        alert("你調用了c的toString函數");  
        return this.i;  
    }  
};  
alert(c);  
           

第六個

alert(bbb == '10'); // true valueOf  
           

這個裡面的判等的順序是,擷取原始值,然後判斷兩邊的原始值是否相等,是以調用valueOf

第七個也就是最後一個

這個裡面的判全等的第一個步驟是判斷類型,因為類型都不一樣了,是以後面什麼都不會調用

讨論

另外,對于原文的這句話“ 在進行對象轉換時(例如:alert(a)),将優先調用toString方法,如若沒有重寫toString将調用valueOf方法,如果兩方法都不沒有重寫,但按Object的toString輸出。”我不是很認同

原文例子

var aa = {   
 i: ,   
 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   
           

我的觀點有些不同,重寫了的toString會被未重寫的其prototype的valueOf隐式調用,而不是優先調用toString。雖然結果不同,但是其實沒有優先級的變化。

var a = {  
        i: ,  
        valueOf: function () {  
            alert("你調用了a的valueOf函數");  
            return Object.prototype.valueOf();  
        },  
        toString: function () {  
            alert("你調用了a的toString函數");  
            return this.i;  
        }  
    };  

    alert(+a);  
    alert(''+a);  
           

結果的确調用了a的toString和a的valueOf,而我們可以很輕易的從最初的原文例子看到,這兩個alert是沒有調用國toString的,那麼我們即可證明重寫了的toString會被未重寫的其prototype的valueOf隐式調用。

另一個原文的例子

var bb = {   
 i: ,   
 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   
           

而valueOf被重寫以後,這個bb的對象沒有toString,自然就會調用其prototype的toString,而那個toString的内容必然是

function toString() {  
    return '[object Object]';  
}  
           

,是以其實這個驗證并沒有什麼意義。無論重寫哪個函數最終的優先級都沒變。

總結

是以那篇的結論并不是最根本的問題,最根本的問題是到底操作對象所處的環境是什麼樣的。

如果要求的是原始值那麼就會調用valueOf,如果要求的是字元串那麼就會調用toString。

繼續閱讀