相等操作符
September 15, 2018
引子
在网上看到了几个关于相等操作符的面试题,除了 1,其他都一头雾水。 再次证明基础不牢固,有必要深入学习一下。
1. []==[] //false
2. []==![] //true
3. {}==!{} //false
4. {}==![] //Uncaught SyntaxError: Unexpected token ==
5. ![]=={} //false
6. []==!{} //true
7. undefined==null //true
操作符运算规则
相等操作符: == 全等操作符: ===
根据 JS 高程里的定义,操作符如上所示,但高程里全等操作符的计算规则有点语焉不详,查询 ECMA 后列举如下:
全等操作符判定规则 - Strict Equality Comparison(SEC)
- 如果
x, y
类型不同,返回false
- 如果
x, y
为undefined
,返回true
- 如果
x, y
为null
,返回true
-
如果
x, y
为number
类型,那么:- 如果
x, y
其中之一,或二者均为NaN
,返回false
- 如果
x
与y
的值相同,返回true
- 如果
x
为+0
,y
为-0
,返回true
- 如果
x
为-0
,y
为+0
,返回true
- 其他情况返回
false
- 如果
- 如果
x, y
为string
类型,那么当二者长度相同且字符顺序也一致时,返回true
;否则返回false
- 如果
x, y
为Boolean
类型,那么当x, y
均为true
或均为false
时,返回 true;否则返回false
x, y
为对象,二者指向同一对象时返回true
;否则返回false
例 1
1 === []; //false
//1为number,[]为对象,类型不同,根据SEC-1返回false
undefined === undefined; //true
//类型相同,根据SEC-2返回true
null === null; //true
//类型相同,根据SEC-3返回true
null === undefined; // false
//null为object,undefinedw为undefined,类型不同,根据SEC-1返回false
NaN === 1; //false
//类型相同,根据SEC-4.1返回false
NaN === NaN; //false
//类型相同,根据SEC-4.1返回false
//NaN是唯一与自身不相等的数
1 === true; //false
//1为number,true为Boolean,类型不同,返回false
'' === false; //false
//''为string,false为Boolean,类型不同,返回false
相等操作符判定规则 - Abstract Equality Comparison(AEC)
- 如果 x, y 类型相同,按全等操作符判定规则进行比较
- 如果 x, y 一方为
undefined
,另一方为null
,返回true
- 如果 x, y 一方为
number
,另一方为string
,将string
转换为number
后再做比较 - 如果 x, y 一方为
Boolean
,将其转换为number
后再做比较 - 如果 x, y 一方为对象,另一方为
string, number, symbol
,将对象转换为基本类型后再做比较 - 其他情况返回
false
AEC-5
中涉及对象与基本类型的转换,ECMA
中由内置方法ToPrimitive(x)
实现,基本规则如下:
对象转换基本类型规则 - Object To Primitive(OTP)
- 如果
x
为日期Date
类型,调用x.toString()
返回日期字符串 x
为其他类型对象,首先调用x.valueOf()
,如结果为基本类型,返回该值- 如果
2
不满足,继续调用x.toString()
,如结果为基本类型,返回该值 -
如
3
不满足,抛出TypeError
异常注:
- 对象的
valueOf()
方法通常返回自身,因此绝大多数情况,转换为基本类型调用的是toString()
方法 - 对象的默认
toString()
方法为Object.prototype.toString()
,其输出值为字符串'[object objType]'
,其中objType
为对象的类型,所以空对象{}.toString()
的输出为'[object Object]'
(注意一个是小写o
,一个是大写O
) - 数组对象
Array
重写了toString()
方法,其输出为Array.join('')
;如果不重写,其输出应为'[object Array]'
- 对象的
例 2
undefined == null; //true
//根据AEC-2,返回true
1 == true; //true
//根据AEC-4,将Boolean转换为number,Number(true)为1,变为1 == 1
//根据AEC-1及SEC-4.2,返回true
'' == false; //true
//根据AEC-4,将Boolean一方转换为number,Number(false)为0,变为'' == 0
//根据AEC-3,将''转换为number,Number('')为0, 变为0 == 0
//根据AEC-1及SEC-4.2,返回true
'' == []; //true
//根据AEC-3, OTP-注-2, OTP-注-3,空数组对象[]调用toString()方法
//[].toString()输出为'',变为'' == ''
//根据AEC-1及SEC-5,返回true
'' == {}; //false
//根据AEC-3, OTP-注-2,空对象{}调用toString()方法,
//{}.toString()输出为'[object Object]',变为'' == '[object Object]'
//根据AEC-1及SEC-5,返回false
解题及分析
规则介绍完了,下面根据规则解题
题 1
[] == []; //false
分析:
- 二者均为数组对象,类型相同
- 二个空数组分别指向
2
个数组,根据SEC-7
,返回false
题 2
[] == ![]; //true
分析:
!
逻辑非运算符先计算,![]
值为false
,表达式变为[] == false
- 类型不同,一方为
Boolean
类型,应用AEC-4
,表达式变为[] == 0
- 一方为对象,应用
AEC-5
,OTP-3
,OTP-注-3
,表达式变为'' == 0
- 继续应用
AEC-3
,表达式变为0 == 0
,返回true
题 3
{}==!{} //false
分析:
!
逻辑非运算符先计算,表达式变为{} == false
- 类型不同,且一方为
Boolean
类型,根据AEC-4
,表达式变为{} == 0
- 一方为对象,应用
AEC-5
,OTP-3
,OTP-注-2
,表达式变为'[object Object]' == 0
- 一方为
number
,应用AEC-3
,表达式变为NaN == 0
- 二者同为
number
类型,应用SEC-4.1
,返回false
题 4
{}==![] //Uncaught SyntaxError: Unexpected token ==
分析:
- 浏览器会将
{}
部分解析为代码块,表达式等效为==![]
,因此报错 - 如果将表达式修改为
({}) == ![]
,浏览器将能正确解析,分析过程及结果同题3
题 5
![] == {}; //false
分析:
!
逻辑非运算符先计算,表达式变为false == {}
- 剩余分析步骤同
题3
,表达式返回false
题 6
[] == !{}; //true
分析:
!
逻辑非运算符先计算,表达式变为[] == false
- 剩余分析步骤同
题2
,返回true
题 7
undefined == null; //true
分析:
- 类型不同,应用
AEC-2
,返回true