我們都知道原始值之間是可以互相轉(zhuǎn)換的,但是如果對象轉(zhuǎn)原始值呢?
- 所有的對象在布爾上下文(context)中均為 true 。所以對于對象,不存在 to-boolean 轉(zhuǎn)換, 只有字符串和數(shù)值轉(zhuǎn)換。
- 數(shù)值轉(zhuǎn)換發(fā)生在對象相減或應(yīng)用數(shù)學(xué)函數(shù)時。例如, Date 對象(將在 日期和時間 一章中介 紹)可以相減, date1 - date2 的結(jié)果是兩個日期之間的差值。
- 至于字符串轉(zhuǎn)換 —— 通常發(fā)生在我們像 alert(obj) 這樣輸出一個對象和類似的上下文中。
所以,由此可見對象轉(zhuǎn)原始值的情況下大致可以分為數(shù)值轉(zhuǎn)換和字符串轉(zhuǎn)換兩種,繼續(xù)往下
什么是hint?
7.1.1.1 OrdinaryToPrimitive ( <var>O</var>, <var>hint</var> )
The abstract operation OrdinaryToPrimitive takes arguments <var>O</var> and <var>hint</var>. It performs the following steps when called:
Assert: <var>hint</var> is either string or number.
-
If <var>hint</var> is string, then
- Let <var>methodNames</var> be ? "toString", "valueOf" ?.
-
Else,
- Let <var>methodNames</var> be ? "valueOf", "toString" ?.
-
For each element <var>name</var> of <var>methodNames</var>, doThrow a TypeError exception.
- Let <var>method</var> be ? Get(<var>O</var>, <var>name</var>).
- If IsCallable(<var>method</var>) is true, then
我們在ECMA規(guī)范中可以得知,hint是類型轉(zhuǎn)換的變體,不太明白?沒關(guān)系,繼續(xù)往下
當(dāng)在對象到字符串的轉(zhuǎn)換中,hint的值便是"string"
<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">var obj = {name:"謝絕先生"}; var anotherObj = {[obj]:"北有極光"};</pre>
當(dāng)在對象到數(shù)字的轉(zhuǎn)換中,hint的值便是"number"
<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">var user = {name:"申屠肆"};
console.log(+user);</pre>
對于不確定轉(zhuǎn)換的類型時,它將依據(jù) hint的值為"default" ,例如,二進(jìn)制加法 + 可用于字符串(連接),也可以用于數(shù)字(相加),所以字符串和數(shù)字這兩 種類型都可以
<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">var sth = {name:"申屠肆"};
console.log(sth + 2);</pre>
接下來,更進(jìn)一步,為了進(jìn)行轉(zhuǎn)換,JavaScript 嘗試查找并調(diào)用三個對象方法:
- Symbol.toPrimitive
- toString
- valueOf
在進(jìn)行類型轉(zhuǎn)換的時候,首先會去查找個名為 Symbol.toPrimitive 的內(nèi)建 symbol,像這樣
<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">obj[Symbol.toPrimitive] = function(hint) { // 返回一個原始值
// hint = "string"、"number" 和 "default" 中的一個
}</pre>
該函數(shù)接收一個參數(shù) hint,hint便是需要進(jìn)行轉(zhuǎn)換的原始值的類型;通過該函數(shù)我們可以完全掌控生成什么樣的原始值,從而達(dá)到我們想要的目的,舉個栗子:
<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">var sth = {
name:"申屠肆",
money:1000,
Symbol.toPrimitive { if (hint == "string") return this.name; else
return this.money;
}
}
console.log(+sth); // 1000
console.log(${sth}); // 申屠肆</pre>
古老的 toString / valueOf
如果沒有 Symbol.toPrimitive ,那么 JavaScript 將嘗試找到它們,并且按照下面的順序進(jìn)行 嘗試: 對于 “string” hint, toString -> valueOf 。 其他情況, valueOf -> toString 。 這些方法必須返回一個原始值。如果 toString 或 valueOf 返回了一個對象,那么返回值會 被忽略(和這里沒有方法的時候相同)。 默認(rèn)情況下,普通對象具有 toString 和 valueOf 方法: toString 方法返回一個字符串 "[object Object]" 。 Symbol.toPrimitive obj[Symbol.toPrimitive] = function(hint) { // 返回一個原始值 // hint = "string"、"number" 和 "default" 中的一個 } let user = { name: "John", money: 1000, Symbol.toPrimitive { alert(
hint: ${hint}); return hint == "string" ?{name: "${this.name}"}: this.money; } }; // 轉(zhuǎn)換演示: alert(user); // hint: string -> {name: "John"} alert(+user); // hint: number -> 1000 alert(user + 500); // hint: default -> 1500 toString/valueOf ● valueOf 方法返回對象自身。
解釋一下:
就是說如果hint 為 "string",在沒有 Symbol.toPrimitive 的情況下,會優(yōu)先查找 toString方法;
其他情況下("default","number"),在沒有 Symbol.toPrimitive 的情況下,valueOf的優(yōu)先級高一些;
至于為什么valueOf和toString方法返回的只能是原始值的情況下,valueOf還會返回對象本身,這是一個歷史問題;
代碼實例:
<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">//Node.js環(huán)境下,根目錄下 index.js文件
let user = {
name:'Joe',
money:1000,
toString() { // "hint" 為 string
console.log('執(zhí)行執(zhí)行~',this.name); return this.name;
},
valueOf() { // hint 為 default 或者 number;
return this.money;
}
}
module.exports = user;</pre>
<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">//與index.js文件同目錄下的測試文件
var assert = require('chai').assert; var user = require('./index');
describe('對象轉(zhuǎn)原始值',function() {
it('hint 為 string的情況下',function() {
assert( ${user} === user.name,'成功的觸發(fā)了toString方法');
});
it('hint 為 default的情況下',function() {
assert( user + 1 === user.money + 1,'成功的觸發(fā)了valueOf方法');
});
it('hint 為 number的情況下',function() {
assert( +user === user.money,'成功的觸發(fā)了valueOf方法');
});
})</pre>
<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">// package.json
{ "name": "something", "version": "1.0.0", "main": "index.js", "license": "MIT", "scripts": { "test":"mocha ./index.test.js" }, "dependencies": { "chai": "^4.3.0", "mocha": "^8.2.1" }
}</pre>
單元測試結(jié)果如下:

2021-02-04 23:56:54
我的博客園地址