类型转换
运算前提:原始值参与运算
当+两边都不是字符串,也不是 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
- +运算符是 特殊的
obj + something → hint = "default"
- 其它数学运算符(- * / % ** 等)
→ 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
优先级
-
[Symbol.toPrimitive](最高优先级)
-
没有 [Symbol.toPrimitive]:根据 hint 决定 valueOf 和 toString 顺序
当 hint === "string",优先调用:
- toString()
- valueOf()
toString() → valueOf()
当 hint === "number" 或 "default",优先调用
- valueOf()
- toString()
例如:
- 数学运算(除 + 号外):- * / % ** > <,hint 为 number
- 加法运算符 +(hint 为 default)
valueOf() → toString()
特殊情况 new Date()
toString() → valueOf()
类型转换原因
类型转换永远要求操作数先变成原始值,不然不允许继续转换。
null + []
分析:
- +号,hint=default,[].valueOf() => [],结果还是本身,不是原始值
- 再调用 toString,[].toString()=>""字符串,得到原始值
- null + "" =>字符串拼接,结果为 "null"
特例说明
{}+1:结果为 1,number 类型
{}是代码块,不参与运算,就变成+1,一元加,所以结果为 1,number 类型
环境区别
- 在表达式上下文中 {} 会被解析为对象,console.log({} + '1')=>"[object Object]1"
- 在浏览器控制台单独输入 {} 会被当成代码块 ,解析成空代码块(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]"
总结
- 模板字符串
${obj},String(obj),"".concat(obj),触发 hint 为 string- [Symbol.toPrimitive]>toString>valueOf
- [Symbol.toPrimitive]内部:string(有 if 条件) || default
- 其余则是 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