跳到主要内容

隐式转换与判等

算术运算符如何做类型转换?​

  • 一般规则: 任一操作数不是数字时, 自动对其执行 Number();
  • 加号特例: + 中任一操作数是字符串时, 另一方执行 String(), 结果为字符串拼接;
1 + "2"; // "12"
1 - "2"; // -1

算术运算的结果规则是什么?​

  • 两个数字: 返回计算结果, 超出可表示范围时得到 Infinity 或 -Infinity;
  • 任一操作数为 NaN: 结果为 NaN;
  • 计算无有效结果 (如负数开平方): 结果为 NaN;
  • 涉及 Infinity: 乘除 0 或无逻辑结果时为 NaN, 其余情况为 Infinity;

关系运算符如何比较不同类型的值?​

  • 数字对数字: 比较数值大小;
  • 字符串对字符串: 逐 code unit 比较, 即比较首个不同字符的编码;
  • 数字与字符串: 把字符串转成数字再比较;
  • 任一为对象: 先按 valueOf() 得到数字, 再退回 toString() 得到字符串;
  • 任一为 NaN: 所有关系比较返回 false;

== 的转换与判等规则是什么?​

  • 同类型: 直接按值比较, 对象则比较是否指向同一引用;
  • null 与 undefined: 两者宽松相等, 且不与其它值相等;
  • 数字与字符串: 把字符串转为数字;
  • 布尔值: 先转为数字再比较;
  • 对象与原始值: 对象按 toPrimitive 转为原始值 (先 valueOf 后 toString);
  • 任一为 NaN: == 返回 false, != 返回 true;
null == undefined; // true
null === undefined; // false
"1" == 1; // true
"1" === 1; // false
NaN == NaN; // false

Symbol.toPrimitive 如何介入隐式转换?​

  • 重写入口: 对象可定义 Symbol.toPrimitive 方法, 接收 hint 并按 "number"、"string"、"default" 三种提示返回原始值;
  • 优先级: 定义后优先于 valueOf 与 toString 被调用;
class Bar {
[Symbol.toPrimitive](hint) {
if (hint === "number") return 3;
if (hint === "string") return "string bar";
return "default bar";
}
}
const bar = new Bar();
3 + bar; // "3default bar"
3 - bar; // 0
String(bar); // "string bar"
  • 实用推论: 重写 toString/valueOf 可以改变判等结果, 这也解释了 a == 1 && a == 2 && a == 3 这类技巧, 详见 属性描述符与响应式;