JavaScript 关系运算符与相等运算符强制转换的问题

img

相等运算符的精简规则

1、 如果 xy 中有一个或者两个都为 NaN,则返回 false

1
2
3
4
5
NaN == NaN
=> false

NaN == 1
=> false

2、 如果 xy 皆为 nullundefined 中的一种类型,则返回 true;否则返回 false;

1
2
3
4
5
6
7
8
null == undefined
=> true

null == null
=> true

null == 0
=> false

3、 如果 xy 皆为 String、Number、Boolean 中的某一类型,则将 x,y 中非Number类型的一方使用 ToNumber 函数转化为 Number 类型再进行比较;

1
2
3
4
5
6
7
8
9
10
// 如果 x 的类型为Number,y 的类型为String
x == ToNumber(y)
// 如果 x 的类型为Boolean,y 的类型为Number
ToNumber(x) == y
// 如果 x 的类型为Boolean,y 的类型为String
ToNumber(x) == ToNumber(y)
例如:
"1" == true // true
"123" == true // false
"0" == false // true

4、 如果 x,y 中有一个为 Object,另外一个是 NumberString,则首先使用 ToPrimitive 函数将Object转化为原始类型,再进行比较。

1
2
3
4
5
6
7
// 如果 x 的类型为 Number 或 String,y 的类型是 Object
x == ToPrimitive(y)
// 出现一方是Boolean, 另一方是Object
{} == true // 会报错,把前面的{}视为了代码块
true == {} // false
var a = {}
a == true // false

5、 等于运算符并不总是传递的。 例如,可能有两个不同的String对象,每个都表示相同的String值; 每个String对象将被认为与==运算符的String值相等,但是两个String对象不会相等。 例如:

1
2
3
4
5
new String("a") == "a" and "a" == new String("a")
=> true.

new String("a") == new String("a")
=> false

6、 当 x,y 皆为 Object 类型,则会判断两者是否(指针)指向都一个对象

1
2
3
4
5
6
var a = {}
a == a
=> true

{} == {} // 此时前者{}不是代码块...e
=> false

关系运算符的精简规则

与相等运算符的规则大致一样,但是一方是Number类型,另一方为其他类型是总是为强制转换Number,例如:

1
2
3
4
5
null >= 0
=> true // null 转换为 0

null == 0
=> false // 相等运算符不会将null转换为0

ToPrimitive

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var ToPrimitive = function(obj,preferredType){
var APIs = {
typeOf: function(obj){
return Object.prototype.toString.call(obj).slice(8,-1);
},
isPrimitive: function(obj){
var _this = this,
types = ['Null','Undefined','String','Boolean','Number'];
return types.indexOf(_this.typeOf(obj)) !== -1;
}
};
// 如果 obj 本身已经是原始值,则直接返回
if(APIs.isPrimitive(obj)) {return obj;}

// 对于 Date 类型,会优先使用其 toString 方法;否则优先使用 valueOf 方法
preferredType = (preferredType === 'String' || APIs.typeOf(obj) === 'Date' ) ? 'String' : 'Number';
if(preferredType==='Number'){
if(APIs.isPrimitive(obj.valueOf())) { return obj.valueOf()};
if(APIs.isPrimitive(obj.toString())) { return obj.toString()};
}else{
if(APIs.isPrimitive(obj.toString())) { return obj.toString()};
if(APIs.isPrimitive(obj.valueOf())) { return obj.valueOf()};
}
throw new TypeError('TypeError');
}

++[[]][+[]]+[+[]]==”10”

首先拆分开来描述,它等价于:

1
2
3
++[[]][+[]]
+
[+[]]

第一行与第三行的+[] 可强制转换为 Number 结果为 0,得出下方结论:

1
2
3
++[[]][0]
+
[0]

第一行:[[]][0] 的意思是指获取数组的索引为0的值得出结果:

  • [[]][0]返回[]。根据语言规范,我们说 [[]][0] === [] 是不正确的,但让我们把这个内部数组称作 a,以避免错误的写法。
  • ++[[]][0] == a + 1, 因为 ++ 的意思是”加一”。
  • ++[[]][0] === +(a + 1);换句话说,你得到的永远是个数值( +1 并不一定得到的是个数值,但 ++ 一定是)。

同样,我们可以把这一堆代码简化的更清晰。让我们把 a 换回成 [] :

1
2
3
+([] + 1)
+
[0]

在 JavaScript 里,这也是正确的:[] + 1 === “1”,因为 [] == “” (这相当于一个空的数组的内部元素连接),于是:

1
2
3
+([] + 1) === +("" + 1),并且 
+("” + 1) === +("1"),并且
+("1") === 1

最后:

1
2
3
4
5
1
+
[0] => "0"

1 + "0" === "10"

null >= 0 返回 true, 而null == 0 || null > 0 返回 false

1、 关系运算符 < > 和 相等运算符 == 并不是一个类别的.

2、 关系运算符,在设计上,总是需要运算元尝试转为一个number . 而相等运算符在设计上,则没有这方面的考虑.

3、 最重要的一点, 不要把 拿 a > b , a == b 的结果 想当然的去和 a >= b 建立联系. 正确的符合最初设计思想的关系是 a > b 与 a >= b是一组 . a == b 和其他相等运算符才是一组. 比如 a === b , a != b, a !== b .

1
2
3
null > 0     //  null 尝试转型为number , 则为0 . 所以结果为 false, 
null >= 0 // null 尝试转为number ,则为0 , 结果为 true.
null == 0 // null在设计上,在此处不尝试转型. 所以 结果为false.

参考

王下邀月熊
The Abstract Equality Comparison Algorithm
Why null >= 0 && null <= 0 but not null == 0?