[物件轉原始型別總結] (‘ + obj) === `${obj}`? 不一定!

xinshangshangxin發表於2019-02-17

一個小測試, 試試:

let o = {
  valueOf() {
    return 0;
  },
};
console.log(+o); // 0
console.log(1 + o); // 1
console.log(1 - o); // 1
console.log(`` + o); // `0`
console.log(`${o}`); // `[object Object]`

結論

  1. 當操作需要一個字串時, hint=string, 當操作需要一個數字時, hint=number, 當運算子不確定時hint=default.
  2. 如果存在 obj[Symbol.toPrimitive](hint), 就直接呼叫
  3. 如果 hintstring, 先呼叫 obj.toString(), 沒有再呼叫 obj.valueOf()
  4. 如果 hintnumber, 先呼叫 obj.valueOf(), 沒有再呼叫obj.toString()
  5. 如果 hintdefault, Date 按照 hint=string處理, 其它按照 hint=number 處理
  6. 如果 toString 或者 valueOf 返回的不是原始型別, 則忽略該呼叫, 轉向下一個呼叫, 如果沒有下一個呼叫, 則報錯, 但是 toPrimitive 必須返回原始型別, 否則報錯

詳解

根據上下文, 會有以下轉換 hint

string

當操作需要一個字串時, 物件轉換的 hintstring.

// alert(引數是字串)
alert(obj);
confirm(obj);

// 物件的屬性是字串
anotherObj[obj] = 123;

number

當操作需要一個數字時, 物件轉換的 hintnumber.

// 明確轉換成數字
Number(obj);
// 轉換成數字(非加法)
+obj;
// 數學運算(加法除外)
1 - obj;
1 * obj;
1 / obj;

因為歷史原因大小比較的 hint 也是 number

// hint 為 number
obj1 > obj2;

default

當運算子不確定時, 物件轉換的 hintdefault.

// 比如加法, 可以是數字相加, 也可以是字串相加
1 + obj;
`1` + obj;

// == 弱相等比較
// obj == string/number/symbol
obj == `1`;
obj == 1;

通常, 內建物件(除了 Date 外), default 轉換 和 number 轉換是相同的
Date 的 default 轉換 和 string 相同 [Date.prototype[@@toPrimitive]](https://developer.mozilla.org…

轉換步驟

  1. 如果存在 obj[Symbol.toPrimitive](hint), 就直接呼叫
  2. 如果 hintstring, 先呼叫 obj.toString(), 沒有再呼叫 obj.valueOf()
  3. 如果 hintnumber, 先抵用 obj.valueOf(), 沒有再呼叫obj.toString()

example

Symbol.toPrimitive

type primitiveType = null | undefined | number | boolean | string | symbol;
type hintType = `string` | `number` | `default`;

obj[Symbol.toPrimitive] = function(hint: hintType): primitiveType {
  console.log(`hint is: ${hint}`);

  return hint == `string` ? `一個字串` : 0;
};

toString / valueOf

let user = {
  name: `John`,
  money: 1000,

  // for hint="string"
  toString(): string {
    return `{name: "${this.name}"}`;
  },

  // for hint="number" or "default"
  valueOf(): number {
    return this.money;
  },
};

alert(user); // toString -> {name: "John"}
alert(+user); // valueOf -> 1000
alert(user + 500); // valueOf -> 1500
let obj = {
  toString() {
    return `2`;
  },
};

// 加法, 呼叫 `default` hint, `default` 和 `number` 轉換相同,
// 先呼叫 valueOf 方法, 因為不存在, 所以呼叫 toString 方法, 返回 "2"
// "2" + 2 = "22"
alert(obj + 2); // "22"

// 存在 valueOf, 所以 2+2 = 4
let obj = {
  toString() {
    return `2`;
  },
  valueOf() {
    return 2;
  },
};

alert(obj + 2); // 4
let d = new Date();
let d2 = d.getTime() - 1;

// 加法, 呼叫 `default` hint, Date 的 `default` 和 `string` 相同
alert(1 + d); // 1Fri Feb 15 2019 20:59:00 GMT+0800 (China Standard Time)

// 減法, 呼叫 `number` hint
alert(d - d2); // 1

參考文件


文章若有紕漏請大家補充指正,謝謝~~
http://blog.xinshangshangxin.com SHANG 殤

相關文章