Javascript中==是一种比较复杂的运算,他的运算规则容易让人犯错!
在详细介绍前我们来复习一下JS中关于数据类型的知识:
- JS中的值有两种类型:基本类型、引用类型(对象类型)
- 基本类型包括:Undefined、Null、Boolean、Number和String等五种类型
- Undefined类型和Null类型都只有一个值,即undefined和null;Boolean类有两个值:true和false;Number类型的值很多很多;String类型的值有无数个值(理论上)
- 所有对象都有valueOf()和toString()方法,它们继承自Object,当然也可能被子类重写
现在考虑表达式:
x == y
其中x和y是六种类型中某一种类型的值。
当x和y的类型相同时,x == y可以转化为x === y,而后者是很简单的(唯一需要注意的可能是NaN),所以下面我们只考虑x和y的类型不同的情况。
原始类型是一种单纯的类型,它们直接了当、容易理解。然而缺点是表达能力有限,难以扩展,所以就有了对象。对象是属性的集合,而属性本身又可以是对象。所以对象可以被构造得任意复杂,足以表示各种各样的事物。
但是,有时候事情复杂了也不是好事。比如一篇长长的论文,并不是每个人都有时间、有耐心或有必要从头到尾读一遍,通常只了解其中心思想就够了。于是论文就有了关键字、概述。JavaScript中的对象也一样,我们需要有一种手段了解它的主要特征,于是对象就有了toString()和valueOf()方法。
toString()方法用来得到对象的一段文字描述;而valueOf()方法用来得到对象的特征值。
当然,这只是我自己的理解。另外,顾名思义,toString()方法倾向于返回一个字符串。valueOf()方法呢?根据规范中的描述,它倾向于返回一个数字——尽管内置类型中,valueOf()方法返回数字的只有Number和Date。
当一个对象与一个非对象比较时,需要将对象转化为原始类型(虽然与布尔类型比较时,需要先将布尔类型变成数字类型,但是接下来还是要将对象类型变成原始类型)。这也是合理的,毕竟==是不严格的相等比较,我们只需要取出对象的主要特征来参与运算,次要特征放在一边就行了。
在运算过程中,所有类型的值都有一种向数字类型转化的趋势。毕竟曾经有名人说过:万物皆数。
举个栗子
前面废话太多了,这里还是举个例子。
例,计算下面:
[''] == false
首先,两个操作数分别是对象类型和布尔类型。需要将布尔类型转为数字类型,而false转为数字的结果是0,所以表达式变为:
[''] == 0
两个操作数变成了对象类型和数字类型。需要将对象类型转为原始类型:
首先调用[].valueOf(),由于数组的valueOf()方法返回自身,所以结果不是原始类型,继续调用[].toString()。
对于数组来说,toString()方法的算法,是将每个元素都转为字符串类型,然后用’,’依次连接起来,所以最终结果是空字符串’’,它是一个原始类型的值。
此时,表达式变为:
'' == 0
两个操作数变成了字符串类型和数字类型,需要将字符串类型转为数字类型,前面说了空字符串变成数字是0。于是表达式变为:
0 == 0
到此为止,两个操作数的类型终于相同了,结果明显是true。
从这个例子可以看出,要想掌握==运算的规则,需要记住那些内置对象的toString()和valueOf()方法的规则。包括Object、Array、Date、Number、String、Boolean等。
总结
- undefined == null的结果是true。它俩与其他所有值比较的结果都是false。
- 字符串 == 数字时,字符串转为数字。
- 布尔值 == 其他类型时,布尔值转为数字。
- 对象 == 数字/字符串时,对象转为基本类型。