很多人一直分不清到底是什麼是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。