Js隐式类型转换

320 阅读3分钟

前言

前阵子,在实习时,看到公司代码通过在字符串前添加一个加号,完成字符串类型转换为数字类型。比如+'123',输出为数字123。后面查阅相关资料,发现原来是Js的隐式类型转换。

+号运算规则

  1. 当一侧为String类型,被识别为字符串拼接,并会优先将另一侧转换为字符串类型。
  2. 当一侧为Number类型,另一侧为原始类型,则将原始类型转换为Number类型。
  3. 当一侧为Number类型,另一侧为引用类型,将引用类型和Number类型转换成字符串后拼接。
  4. 如果+xxx这种形式,会把xxx转化为number类型。比如+{}=NaN
console.log(3+true) // 4 true转化为1
console.log(3+'4') //34 3转化为字符串
console.log(3+{}) //3[Object Object]
console.log(3+[]) //3(string类型) []转化为""
console.log(+[]) // 0
console.log(+'123b') // NaN

特例

/*
原因:
这里的{a: 3}会被浏览器认为是区块语句而不是对象字面量,
即不认为是个对象,即对应了上面的第四点。
{a: 3}+['3']相当于+['3']
+['3']会进行toPrimitive操作转化为'3'(toPrimitive操作下面会说到)
'3'转化为number类型
最终得到number类型的3
*/
console.log({a: 3}+['3']) // 3(number类型)

const b = {a: 3}
console.log(b+['3']) // [Object Object]3

除乘减运算规则

两个操作数都需要转化为number类型。

console.log('4'/'2') // 2
console.log(['4']/'2') // 2
console.log(2/{})// NaN
console.log(null-3)// -3 null会转化为0

==转换规则

  1. 对于numberstring比较,将字符串转换为number类型后再进行比较;
  2. 对于boolean和其他类型的比较,将其boolean类型转换为number类型后再进行比较;
  3. 对于引用类型和原始类型的比较,将对象进行ToPrimitive操作后再进行比较;
  4. 引用类型之间的比较,判断是否指向同一个地址。
// 建议看了下面ToPrimitive转换规则再回来看
// ![]为false false转化为0(规则2) []转化为0(规则1)
console.log([] == ![]) //true 
// false转化为0(规则2) [undefined]通过toString变成''(规则3) ''变为0(规则1)
console.log([undefined] == false)// true
// 规则4
console.log({} == {}) // false

引用类型转换原始值--ToPrimitive

ToPrimitive

ToPrimitive操作就是将引用类型转换为原始值。

// input是调用对象,PreferredType为期待返回的结果类型
ToPrimitive(input, PreferredType)

执行过程

  1. 如果input不是引用类型,直接返回结果;
  2. 根据PreferredType定义hint值;
    • 如果PreferredTypenumberhintnumber
    • 如果PreferredTypestringhintstring
    • 其他,hintdefualt
  3. 判断input是否有Symbol.toPrimitive方法,如果有则根据hint的值执行得到对应的结果,否则抛出TypeError错误。

代码来源:Symbol.toPrimitive - JavaScript | MDN (mozilla.org)

// 一个没有提供 Symbol.toPrimitive 属性的对象,参与运算时的输出结果。
const obj1 = {};
console.log(+obj1); // NaN
console.log(`${obj1}`); // "[object Object]"
console.log(obj1 + ""); // "[object Object]"

// 接下面声明一个对象,手动赋予了 Symbol.toPrimitive 属性,再来查看输出结果。
const obj2 = {
  [Symbol.toPrimitive](hint) {
    if (hint === "number") {
      return 10;
    }
    if (hint === "string") {
      return "hello";
    }
    return true;
  },
};
console.log(+obj2); // 10  — hint 参数值是 "number"
console.log(`${obj2}`); // "hello"   — hint 参数值是 "string"
console.log(obj2 + ""); // "true"    — hint 参数值是 "default"
  1. 如果hintdefault,则改为number。也就是说默认是按number。除了Date对象,和Symbol对象。Date对象转换时hint参数是StringSymbol对象进行类型转换会抛出异常;
  2. 执行OrdinaryToPrimitive运算。
    • 如果hint是'string',调用toString方法,得到的是原始数据类型,返回该值;否则调用valueOf方法,如果得到的是原始数据类型,返回该值;否则抛出异常;
    • 如果hint是'number',则相反;先调用valueOf方法,得到的是原始数据类型,返回该值;否则调用toString方法,如果得到的是原始数据类型,返回该值;否则抛出异常。

valueOf 与 toString 返回值

类型valueOftoString
Object对象本身[object type] type指的是对象本身的类型识别
Array对象本身与join(',')方法返回值一致
Number对象本身字符串值,可传入一个参数,决定其进制
String对象本身对象本身
Boolean对象本身返回"true"或"false"字符串

转换表

最后附上转换表~ convert-table (1).png

总结

严格等于(===)万岁!!!

参考链接

  1. 聊聊JS中的ToPrimitive原始值转换方法 - 掘金 (juejin.cn)
  2. Symbol.toPrimitive - JavaScript | MDN (mozilla.org)
  3. JavaScript 隐式类型转换,一篇就够了! (freecodecamp.org)