理解JS 中相等和全等操作符比较规则
在日常的 js 编码过程中,可能很难看到相等运算符(=)是如何工作的。特别是当操作数具有不同类型时。这有时会在条件语句中产生一些难以识别的 bug。很容易理解为什么 0 == 8 是 flase 的或者 '' == false 是 true。但是为什么{} == true是 false 的就看不出来了。接下将会讲这是肿么肥事。
在这之前,先说几个术语:
- 操作符(Operator) 表示操作的符号。例如,相等运算符==比较两个值,三等运算符 === 比较两个值及其类型,加法运算符+两个数字和或连接两个字符串。
- 操作数(Operand) 是运算的主体,是执行运算的数量。例如,在表达式 0 == {} 中,0 是第一个操作数,{} 是第二个操作数。
- js 中的基本数据类型(原始类型)有 number,string, boolean,null 和 undefined,symbol。
全等运算符 ===
全等和不全等操作符遵循以下基本规则(IEA规则):
- 如果两个操作数有不同的类型,它们不是严格相等的
- 如果两个操作数都为 null,则它们是严格相等的
- 如果两个操作数都为 undefined,它们是严格相等的
- 如果一个或两个操作数都是 NaN,它们就不是严格相等的
- 如果两个操作数都为 true 或都为 false,它们是严格相等的
- 如果两个操作数都是 number 类型并且具有相同的值,则它们是严格相等的
- 如果两个操作数都是 string 类型并且具有相同的值,则它们是严格相等的
- 如果两个操作数都引用相同的对象或函数,则它们是严格相等的
- 以下所有其他情况下操作数都不是严格相等的。
规则很简单。
值得一提的是,在全等运算中,NaN 与其他任何值相比,结果都是 false。 来看看考虑些例子,这是学习这些规则的好方式。
例 1
1 === "1" // false, 规则 1
操作数是不同的类型(数字和字符串),基于 IEA 规则1,它们是不等的。
例 2
0 === 0 // true, 规则 6
操作数具有相同的类型和相同的值,因此根据IEA规则6,它们是严格相等的。
例 3
undefined === undefined // true, 规则 3
两个操作数都是 undefined 的,应用 IEA 规则3,它们是相等的。
例 4
undefined === null // false, 规则 1
因为操作数是不同的类型,根据IEA规则1,它们并不相同。
例 5
NaN === NaN // false, IEA 规则 5
操作数是相同的类型,但是IEA 规则4 表明任何与 NaN 比较都是不相等的。
例 6
var firstObject = {},
secondObject = firstObject;
secondObject['name'] = 'Neo';
secondObject === firstObject // true, IEA 规则 8
两个变量 firstObject 和 secondObject 都是对同一对象的引用,根据 IEA 规则8,它们相等。
例 7
[] === [] //false, IEA 规则 9
字面量 [] 创建了一个新的数组引用。这两个操作数是相同的类型(对象),但是它们引用不同的对象。根据 IEA 规则 9 ,它们不相等。
对象转换为原始值的规则
对象到布尔值
对象到布尔值的转换非常简单:所有的对象(包括数字和函数)都转换为 true。对于包装对象亦是如此:new Boolean(false) 是一个对象而不是原始值,它将转换为 true。
对象到字符串
对象到字符串 和 对象到数字 的转换都是通过调用待转换对象的一个方法来完成的。一个麻烦的事实是,JS 对象有两个不同的方法来执行转换,接下来要讨论的一些特殊场景更加复杂。值得注意的是,这里提到的字符串和对象的转换规则只适用于原生对象(native object)。宿主对象(例如有Web浏览器定义的对象)根据各自的算法可以转换成字符串和数字。
所有的对象继承了两个转换方法。第一个是toString(),它的作用是返回一个反映这个对象的字符串。默认的 toString() 方法并不会返回一个有趣的值:
({x:1,y:2}).toString() //=>"[object object]"
很多类定义了更多特定版本的toString()方法。例如,数组的 toString() 方法是将每个数组元素转换为一个字符串,并在元素之间添加逗号后合并成结果字符串。
函数的 toString() 方法返回了这个函数的实现定义。实际上,这里的实现是通常是将用户定义的函数转换为 JS 源代码字符串。
日期 Date 的 toString() 方法返回了一个可读的日期和时间字符串。
RegExp 的 toString() 方法将RegExp对象转换为表示正则表达式直接量的字符串:
来几个例子:
[1,2,3].toString() //=> "1,2,3"
(function(x){ f(x); }).toString() // => "function(x){ f(x); }"
/\d+/g.toString() // => "/\d+/g"
new Date(2019,9,16).toString() //=> "Wed Oct 16 2019 00:00:00 GMT+0800 (中国标准时间)"
另一个转换对象的函数是 valueOf()。如果存在任意原始值,它就默认将对象转换为表示它的原始值。对象是复合值,而且大多数对象无法真正表示为一个原始值,因此默认的 valueOf() 方法简单地返回对象本身,而不是返回一个原始值。数组、函数和正则表达式简单地继承了这个方法,调用这些类型的实例的valueOf() 方法只是简单返回对象本身。日期 Date 的 valueOf() 方法会返回它的一个内部表示:1970年1月1日以来的毫秒数。
new Date(2019,9,16).valueOf() // 1571155200000
通过使用 toString() 和 valueOf() 方法,就可以做到对象到字符串和对象到数字的转换了。但需要注意的是,在某些特殊的场景中,JS 执行了完全不同的对象到原始值的转换。
JS 中对象到字符串的转换经过如下这些步骤,咱们简称 OPCA 算法。
- 如果方法 valueOf() 存在,则调用它。如果 valueOf() 返回一个原始值,JS 将这个值转换为字符串(如果本身不是字符串的话),并返回这个字符串结果。
- 如果方法 toString() 存在,则调用它。如果 toString() 返回一个原始值,JS 将这个值转换为字符串(如果本身不是字符串的话),并返回这个字符串结果。需要注意,原始值到字符串的转换。
- 否则,JS 无法从 toString() 或 valueOf() 获得一个原始值,它将抛出一个 TypeError:不能将对象转换为原始值 异常
当调用 valueOf() 方法时,大多数原生对象都会返回对象本身。因此 toString() 方法使用得更频繁。
关于 Date 对象的注意事项:在转换为原始值时,对象立即使用 toString() 方法转换为字符串。这样,规则1就被跳过了。普通的 JS 对象,{}或 new object(),通常被转换成 "[object Object]"
数组通过将它的元素与“,”分隔符连接转换为。例如 [1,3,"four"] 被转换成" 1,3,four"。
相等运算符 ==
相等运算符 “==” 如果两个操作数不是同一类型,那么相等运算符会尝试一些类型转换,然后进行比较。
相等运算符算法(EEA)
- 如果操作数具有相同的类型,请使用上面的 IEA 测试它们是否严格相等。 如果它们不严格相等,则它们不相等,否则相等。
- 如果操作数有不同的类型:
2.1如果一个操作数为 null 而另一个 undefined,则它们相等
2.2如果一个值是数字,另一个是字符串,先将字符串转换为数字,然后使用转换后的值比较
2.3如果一个操作数是布尔值,则将 true 转换为 1,将 false 转换为 0,然后使用转换后的值比较
2.4如果一个操作数是一个对象,而另一个操作数是一个数字或字符串,则使用OPCA将该对象转换为原原始值,再使用转换后的值比较 - 在以上的其他情况下,操作数都不相等
例 1
1 == true // true
上面的转换步骤:
- 1 == true (使用EEA 规则2.3 将 true 转换为 1)
- 1 == 1(操作数有相同的类型。使用 EEA 规则1 将相等转换为全等运算进行比较
- 1 === 1(两个操作数都是数字,并且具有相同的值。根据 IEA 规则 6,这是相等的)
- true
例 2
'' == 0 // true
上面的转换步骤:
- '' == 0(一个操作数是字符串,另一个操作数是数字,根据EEA规则2.2,'' 被转换为数字 0 )
- 0 == 0(操作数类型相同,使用 EEA规则1 将相等转换为全等运算进行比较)
- 0 === 0(操作数类型相同,值相同,所以根据IEA规则6,它是一个恒等式)
- true
例 3
null == 0 // false
上面的转换步骤:
- null == 0 (null 是原始类型,0 是 number 类型。根据EEA规则3)
- false
例 4
null == undefined // true
上面的转换步骤:
- null == undefined(基于EEA规则2.1,操作数相等)
- true
例 5
NaN == NaN // false
上面的转换步骤:
- NaN == NaN(两个操作数都是数字。根据EEA规则1,将相等转换为全等运算进行比较)
- NaN === NaN(根据IEA规则4,操作数严格不相等)
- false
例 6
[''] == '' // true
上面的转换步骤:
- [''] == ''(['']是一个数组和 '' 是一个字符串。应用EEA规则2.4并使用OPCA规则2将数组转换为原始值 '')
- '' == '' (两个操作数都是字符串,将相等转换为全等运算进行比较)
- '' === '' (两个操作数类型相同,值相同。使用IEA规则7,它们是相等的)
- true
例 7
{} == true // false
上面的转换步骤:
- {} == true(使用EEA规则2.3,将 true 操作数转换为 1)
- {} == 1(第一个操作数是一个对象,因此有必要使用OPCA将其转换为原始值)
- “[object object]”== 1(因为第一个操作数是字符串,第二个操作数是数字,根据 EEA规则2.2 将“[object object]”转换为数字)
- NaN == 1(两个操作数都是数字,因此使用 EEA规则1 将相等转换为全等运算进行比较)
- NaN === 1(根据 IEA规则4,没有什么是与 NaN 相等的,结果是 false)
- false
东莞vi设计https://www.houdianzi.com/dgvi/ 豌豆资源网站大全https://55wd.com
实用技巧
即使在详细研究了本文中的所有示例、学习了算法之后,你会发现要立即理解复杂的比较还需要时间的积累。
告诉你一些技巧。 将本文添加到书签中(使用Ctrl + D),下一次看到有趣的情况时,可以根据等式算法编写逐步的计算。 如果检查至少 10 个示例,则以后不会有任何问题。
现在就可以试试,如 [0] == 0 的结果和转化步骤是什么?
相等运算符==进行类型转换。因此,可能会产生意想不到的结果,例如 {}== true 是 false( 参见例7)。在大多数情况下,使用全等操作符 === 更安全。
总结
相等和全等运算符号可能是最常用的运算符之一。理解它们是编写稳定且bug较少的 JS 的步骤之一。

浙公网安备 33010602011771号