JavaScript == 和 ===

== 操作符(Equality,相等操作符)

相等操作符会做类型转换。

我们先来看看什么是类型转换(type coercion)

当操作符两边的操作数是不同类型时,其中一个操作数将转换为另一个操作数同类型的“等效”值。比如:

12 - '3' // 9, 将 string 类型转换成 number 类型,相当于 12 - Number('3')
12 - 'hello' // NaN, 因为 Number('hello') 为 NaN,12 - NaN 为 NaN
'3' - 12 // -9, 同样转成 number 类型
12 - true // 11, Number(true) 为 1
false - 12 // -12, Number(false) 为 0
12 + '3' // 123, 将 12 转换成 string 类型,再连接 '3'

看上去很简单对不对?减法将非 number 类型的转换成 number 类型的,加法将 number 类型转换成 string 类型的,真的这样吗?看下面:

12 + true // 13,这里将布尔类型 true 转换成数值类型 1,相当于 12 + Number(true)

从上面可以看出,“加法将 number 类型 转换成 string 类型”这个总结并不正确,事实上,类型转换并不单单看操作符,也就是并不只是看你做加法还是做减法,还要看你的类型,我们从上面的规律中做下总结:

  • number - string 将 string 转换成 number
  • string - number 将 string 转换成 number
  • number - boolean 将 boolean 转成 number
  • boolean - number 将 boolean 转成 number
  • number + string 将 number 转成 string
  • number + boolean 将 boolean 转成 number
  • ...

有点眼花,也不是很有规律。有时候类型转换还可能会调用 toString 方法,加上操作符有很多,像 +、-、*、/、%、、= 等等,再加上 5 种基本数据类型 Number、String、Undefined、Null、Boolean,还有引用类型 Object,这样组合起来,类型转换的规律就很难掌握了。

比如下面这个:

[1] - 3 // -2, 相当于 Number([1].toString()) - 3
[1, 2, 3] - 3 // NaN, 这里 toString 之后 会得到 '1,2,3',转成 number 就是 NaN
[[]] - 3 // -3, [[]].toString 是 "","" 转成 number 类型是 0
5 * '1' // 5, Number('1') 为 1
5 * 'true' // NaN, Number('true') 为 NaN
5 * true // 5, Number(true) 为 1

是不是觉得规则已经比较难记忆了?

回到我们的 == 操作符

相等操作符在比较时,如果左右两边的类型不同,也会将左边或者右边的操作数转换成与对方同类型的等效值。比如:

12 == "12" // true, string 类型转换成 number 类型
1 == true // true, boolean 类型转换成 number 类型
true == '1' // true, 两者都转换成 number 再比较? 还是 '1' 转换成 boolean 类型?
true == '2' // false?? Boolean('2') 可是等于 true 哦,结合上面一条语句,可以得出应该是两者都转换成 number 再比较

再来看看 == 操作符的传递性

0 == '' // true
0 == '0' // true
'' == '0' // false

可以看出,== 操作符不满足传递性

再来看几个例子:

false == 'false'    // false
false == '0'        // true
false == undefined  // false
false == null       // false
null == undefined   // true
' \t\r\n ' == 0     // true

是不是觉得还能理解,但可能记不住了?所以,如果你不了解所有的规则,最好不要使用相等操作符,而是使用恒等操作符

规则这么多,掌握不好容易出错。

=== 操作符(Identity,恒等操作符)

很多人认为恒等操作符代表值相等,并且有相同的类型,但这是不对的,来看下面的例子:

var a = [1, 2, 3],
    b = [1, 2, 3],
    c = {},
    d = {};
console.log(a === b); // false
console.log(c === d); // false

可见,即使是相同的类型,而且值相等,也不一定恒等。

恒等

恒等操作符有三种情况:

  1. 两个操作数都是引用类型,他们都引用同一个 object,即地址相同,则恒等成立。
  2. 两个操作数都是基本数据类型 (Boolean、Number、String、Undefined、Null),如果值相同,则恒等成立。
  3. 一个操作数是引用类型,另一个是基本数据类型,恒等不成立。

上面的例子不等是因为地址不同。我们再来看看比较特殊的 String:

String 的基本数据类型和基本包装类型

我们知道,基本数据类型不是引用类型,应该是没有方法的,比如 'hello world',但你确实可以用 'hello world'.split("") 分割字符串,这是因为调用 split 这个方法的时候,后台就会创建一个基本包装类型的对象 (new String('hello world')),我们实质上是从这个基本包装类型里获取的方法,这样的基本包装类型还有 Boolean 和 Number。

要明确,'hello world' 只是一个基本数据类型,不管它是拼接而成还是直接以字面量的形式形成,比如:

var a = 'hello' + ' world',
    b = 'hello world';
console.log(a === b); // true

因为 a、b都是基本数据类型,所以直接比较值,是相等的,那么恒等成立。

但如果是这样:

var a = 'hello world',
    b = new String('hello world');
console.log(a === b); // false, 因为 a 是基本数据类型,b 是引用类型,恒等不成立。

来看一下恒等操作符的比较情况:

所以 Douglas Crockford 建议我们永远不使用 == 操作符,而是使用 === 操作符

另外,在引用类型的比较上 == 和 === 的表现还是一致的,因为不论是否有类型转换,值都是地址,即比较的都是地址,地址相同则相同,如有不对,还望指出:

var a = [1, 2, 3],
    b = [1, 2, 3],
    c = {},
    d = {},
    e = a;
console.log(a === b); // false
console.log(c === d); // false
console.log(a == b); // false
console.log(c == d); // false
console.log(a == e); // true
console.log(a === e); // true
console.log([0] == (new String("0"))); // false
console.log([0] === (new String("0"))); // false


上一篇:小视频源码,可控的跑马灯,无需焦点


下一篇:褪去光环——抖音开发,android 短视频开发中的“黑科技”