Javascript对象类型转换

48 阅读4分钟

类型转换

运算前提:原始值参与运算

+两边都不是字符串,也不是 BigInt 时,双方都会转换为 number,然后执行加法,前提是基本数据类型

js 中有三种 hint

  • string
  • number
  • default

hint 是什么

在对象要转换为 原始值(ToPrimitive)时,JS 会告诉对象

我现在需要你变成一个原始值,你最好给我一个 数字 或者 字符串。 你要根据我的‘提示(hint)’来决定你返回哪种值。

明确 hint 的值,就能明确 toString() 和 valueOf() 的调用顺序

hint="string"时

模板字符串 ${},String(),"".concat(obj)

Symbol.toPrimitive("string") > toString() > valueOf()

hint="number"时(数学运算)

Symbol.toPrimitive("number") > valueOf() > toString()

hint = "default" 时(加号/比较等通用情况)

Symbol.toPrimitive("default") > valueOf() > toString()

对象默认没有 Symbol.toPrimitive。 对象只有原型上的 valueOf/toString。

obj + x

hint = "default"

obj + 1

这是一个典型的 数字运算场景。

JS 先尝试对 obj 进行 ToPrimitive(hint = "default")(不是 number!)

${obj}

hint = "string"

对于对象的运算

+既能做数字运算,也能做字符串拼接,所以 JS 不选择 "number",而是先用 "default",正好解释上面obj + 1

  1. +运算符是 特殊的

    obj + something → hint = "default"

  2. 其它数学运算符(- * / % ** 等)

    → hint = "number"(因为没有字符串拼接的可能)

只有在 “确定是纯数字运算” 的情况下,hint 才是 "number"。

承上启下,举例说明;确定 hint 值,和调用顺序优先级

Symbol.toPrimitive方法

[Symbol.toPrimitive]优先级最高,下面手动添加 valueOf 和 toString 也是为了辅助证明

明确 hint 值后,参考上面 hint 不同值时,各方法的调用顺序

注意:把对象转为为原始值后,在根据运算符号进行隐式类型转换

let obj = {
  [Symbol.toPrimitive](hint) {
    if (hint === "string") return "S";
    if (hint === "number") return 99;
    return "D";
  },
  valueOf() {
    return 11;
  },
  toString() {
    return "hello";
  },
};

console.log(obj + ""); //hint:default, 输出 → "D"
console.log(`${obj}`); // hint:string ,输出→ "S"
console.log(obj + 1); // hint:defalut, 输出-> "D1"

没有Symbol.toPrimitive方法

let obj = {
  valueOf() {
    return 11;
  },
  toString() {
    return "hello";
  },
};
//console.log(obj + "") + ,hint为default,调用valueOf()方法转化为原始值为数字11,
// 11 + "" ,字符拼接,数字11隐式转换为字符串"11",所以结果为字符串"11"
console.log(obj + ""); //hint:default, 输出 → "11"
console.log(`${obj}`); // hint:string ,输出→ "hello"
console.log(obj + 1); // hint:defalut, 输出-> 12

优先级

  1. [Symbol.toPrimitive](最高优先级)

  2. 没有 [Symbol.toPrimitive]:根据 hint 决定 valueOf 和 toString 顺序

当 hint === "string",优先调用:

  1. toString()
  2. valueOf()

toString() → valueOf()

当 hint === "number" 或 "default",优先调用

  1. valueOf()
  2. toString()

例如:

  • 数学运算(除 + 号外):- * / % ** > <,hint 为 number
  • 加法运算符 +(hint 为 default)

valueOf() → toString()

特殊情况 new Date()

toString() → valueOf()

类型转换原因

类型转换永远要求操作数先变成原始值,不然不允许继续转换。

null + []

分析:

  1. +号,hint=default,[].valueOf() => [],结果还是本身,不是原始值
  2. 再调用 toString,[].toString()=>""字符串,得到原始值
  3. null + "" =>字符串拼接,结果为 "null"

特例说明

{}+1:结果为 1,number 类型

{}是代码块,不参与运算,就变成+1,一元加,所以结果为 1,number 类型

环境区别

  1. 在表达式上下文中 {} 会被解析为对象,console.log({} + '1')=>"[object Object]1"
  2. 在浏览器控制台单独输入 {} 会被当成代码块 ,解析成空代码块(block statement),而不是对象字面量。

表达式内部(如函数参数、括号内、赋值右侧):对象字面量 {}

语句开头:代码块 { ... }

常见对象的 valueOf / toString 表现

类型valueOf() 返回toString() 返回哪个更常用
Object对象本身"[object Object]"toString
Array数组本身"1,2,3"(join)toString
Function函数本身(function)函数字符串源码toString
Date数字时间戳(valueOf 优先)日期字符串valueOf
Number原始值 ✔原始值字符串valueOf
String原始值 ✔原始值 ✔valueOf

【常见对象】转换为原始值说明

  • 数组:valueOf 无效,toString = join

    [1,2,3].toString()等价于[1,2,3].join()=> "1,2,3"

  • 函数:valueOf 返回函数本身(无效),toString 返回源码

    (function foo(){return 'foo'}).toString()=>"function foo(){return 'foo'}"

  • 对象:valueOf 返回对象本身(无效),toString 返回 "[object Object]"

总结

  1. 模板字符串${obj}String(obj),"".concat(obj),触发 hint 为 string
    • [Symbol.toPrimitive]>toString>valueOf
    • [Symbol.toPrimitive]内部:string(有 if 条件) || default
  2. 其余则是 hint 分别为 default 和 number
    • [Symbol.toPrimitive]>valueOf>toString
    • [Symbol.toPrimitive]内部:分别是number(有 if 条件) || default,和default

下面例子注释if条件语句,无论hint为什么,都是默认default,返回字符串"D"

let obj = {
  [Symbol.toPrimitive](hint) {
    // if (hint === "string") return "S";
    // if (hint === "number") return 99;
    return "D";
  },
};

==类型转换

null 只等于 undefined

null == undefined // true,其他全为false

基本数据类型比较优先级转换

  • 原始值比较,遇到Boolean会转换为数字。
  • 数字>字符串。字符串会转数字

对象比较原始值,hint值

hint一律为default,即valueOf>toString。(Date例外)

参考:基于 ECMA262 标准

逻辑运算符!,会触发 ToBoolean(布尔类型强制转换)

!X转换步骤

  • 步骤 1:先将 x 转换为布尔值 → Boolean(x)
  • 步骤 2:取反 → true ⇄ false

!x === !Boolean(x)

"0"==!1    // true
[] == ![]  // true 右边Boolean([])=>true !Boolean([])=>false ;
// == 类型转换布尔转数字,右边等于0;左边类型转换valueof=>toString=>"",""=>0;所以结果为true