当你在 javascript 中用"=="的时候, 你在比较什么?

=====类型转换javascript

发布于 2020

类型转换

  js 中存在强制类型转换和隐式类型转换, 有意识地去调用转换方法或者构造函数去转换称为强制类型转换(implicit coercion), 无意识地转换(语言机制自发完成)称为隐式类型转换(explicit coercion).

const a = 42;
const b = a + ""; // implicit coercion
const c = String(a); // explicit coercion

这里稍后会讨论的"=="问题, 涉及到的也是隐式类型转换. 转换的目标只能是 string, number or boolean. 不可能经过隐式类型转换, 转换出一个复杂类型的数据(Object, Array, Function ...). 现在来看看各个类型的 ToString,ToNumber, ToBoolean, 或者说是 ToPrimitive.

ToString

ToNumber

ToBoolean

先来看一定是 false 的几个值

这个列表外的值, 都是 true(误, 有例外). 复杂类型的值都是 true(误, 有例外)

!!new Boolean(false); // true
!!new Boolean(0); // true

在史前时代, 人们判断是不是 IE 浏览器, 往往用这样的代码:

if (document.all) {
  /* it's IE */
}

结果慢慢地, 别的浏览器也开始有这个 API 了. 可是旧代码已经沉淀下来成了地层中的岩石, 挖出来修改的成本太高了, 干脆在非 IE 浏览器中 document.all 是 falsy 算了, 所以导致了这个对象的奇葩行为.

!!document.all; // true 在IE11以下版本
!!document.all; // false 在IE11以上版本或非IE环境

"==" VS. "==="

两者的区别是: "=="比较的时候, 允许隐式类型转换, "==="不允许隐式类型转换. 稍微提下"==="的两个奇葩行为:

"=="规则

42 === "42"; // false
42 == "42"; // true

问题来了, 42 == '42'到底隐式转换成了什么? 是 42 == 42 还是'42'=='42'? 接下来就详细介绍下转换的规则, 了解这些规则后, "=="很多诡异的行为都变得有理有据, 再也不用视为"糟粕"不敢用了.

String VS. Number

If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y). If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.

如果"=="两边是字符串和数字, 那么字符串转化为数字去比较. 字符串转化为数字的规则, 上边有介绍.

Anything VS. Boolean

If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).

如果"=="两边是 Boolean 值和其他值, 那么第一步会将 Boolean 值转化为数字, 转化的结果只能是 0 或 1. 然后再用 0 或 1 去和其他值比较, 如果其他值是复杂类型的值, 再进行其他转换, 如果是字符串, 参考上一条.

true == "1"; // 1 == '1' -> 1 == 1 true
true == "42"; // 1 == '42' -> 1 == 42 false

null VS. undefined

If x is null and y is undefined, return true. If x is undefined and y is null, return true.

如果是 null 和 undefined 作比较, 返回 true. 这俩哥们和其他的任何值作比较, 都返回 false.

const a = null;
const b = undefined;
a == b; // true
a == null; // true
b == null; // true
a == false; // false
b == false; // false
a == ""; // false
b == ""; // false
a == 0; // false
b == 0; // false

Objects VS. non-Objects

If Type(x) is either String or Number and Type(y) is Object, return the result of the comparison x == ToPrimitive(y). If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x) == y.

当复杂类型与基本类型作比较的时候, 复杂类型值首先要转换成基本类型的值, 转化规则前边有介绍.

["42"] == 42; // true
Object(10) == "10"; // true

两点需要注意, 构造函数的参数是 null 或者 undefined, 会返回一个"空"对象, 所以下边的结果是有道理的.

const a = Object(null); // {}
a == null; // false
const b = Object(undefined); // {}
b == undefined; // false

最后练习

[] == ![]; // true [] == false -> [] == 0 -> "" == 0 -> 0 == 0

2 == [2]; // true  2 == "2" -> 2 == 2

"" == [null]; // true  "" == ""  (ps: String([null]) === "";  String(null) === "null")

"0" == false; // true "0" == 0 -> 0 == 0

false == 0; // true 0 == 0

false == ""; // true 0 == "" -> 0 == 0

false == []; // true 0 == [] -> 0 == "" -> 0 == 0

"" == 0; // true 0 == 0

"" == []; // true "" == ""

0 == []; // true 0 == "" -> 0 == 0