【译】等运算符

在这篇文章中将阐述一些等运算符的技术特点。

众所周知,在ECMAScript中‘等于’是不可传递的。

不可传递的等号

就像当于:

A == B B==C

在有些情况下这并不代表:

A==C

举个例子

1
2
3
4
5
知识兔td>
console.log(
'0'== 0,
0 == '',
'0'== ''// false
);
知识兔td>

再来一个:

1
2
3
4
5
6
7
8
知识兔td>
var a = new String("foo");
var b = "foo";
var c = new String("foo");
console.log(
a == b,
b == c,
a == c // false
);
知识兔td>

使用’==’时会触发隐式的类型转换,所以导致了上述例子的结果。因此我们建议完全不使用‘==’,而应当用‘===’、即严格相等。另外,非严格相等‘==’被称为ECMAScript中的’邪恶部分’。严格‘===’与非严格‘==’
当然了,我们的主要目标(这适用于所有语言)是改善语言的抽象性,以此更容易得理解语言的结构以及减少歧义。程序员不需要记很多关于简单运算符的知识来避免歧义。
最迷惑人的情况就是falsy values(就像0、”、null)和等式右边的布尔值比较的时候。在这种情况下甚至都没有ToBoolean隐式转换。为了解释清楚这个问题,我们先考虑一下在使用‘==’时的普通隐式类型转换。
null和undefined在下面的情况下总是相等。

1
2
3
4
知识兔td>
console.log(
null== undefined,
undefined == null
);
知识兔td>

一个比较表达式的操作数中若有一个是number类型的,那到最后(经过一些中间的类型转换)另外一个操作数总是会被转换成number类型。

toNumber是‘==’的强制转换

实际上,toNumber是‘==’主要使用到的转换。记住这个你就能很容易地确定下来隐式转换后的结果。如果两边的类型不一样,那就顺序给每一个操作数进行toNumber,直到类型都是number型,也就得到正确的结果了。
先从简单的toNumber转换开始

1
2
3
4
知识兔td>
console.log(
1 == "1", // true -> ToNumber("1") -> 1 == 1
"1"== 1, // the same, ToNumber("1") but for the first operand
);
知识兔td>

而对于对象的类型转换来说,先做toPrimitive,接着再toNumber(但除非toPrimitive没有返回一个string并且等式左边是一个string,那就不会再toNumber了,因为等式两边没有一个是number型啊没理由toNumber)。而toPrimitive依靠对象的内部方法[[DefaultValue]],这个方法返回对象toString或者valueOf的结果。

1
2
3
4
5
6
知识兔td>
console.log(
1 == {}, // false, default valueOf/toString
1 == ({valueOf: function() {return1;}}),
1 == ({toString: function() {return"1";}}),
1 == [1] // true, with default valueOf/toString
);
知识兔td>

Not ToBoolean but still ToNumber

关于布尔型,还是那句话,没有toBoolean转换,依然是toNumber。
记住true的toNumber是1,false的toNumber是0。

1
2
3
4
5
6
7
8
9
10
知识兔td>
console.log(
1 == true, // true, ToNumber(true) -> 1 == 1
0 == false, // true, ToNumber(false) -> 0 == 0
// more interesting examples
"001"== true, // true, ToNumber("001") == ToNumber(true) -> 1 == 1
"002"== true, // false, by the same conversion chain -> 2 != 1
"0x0"== false, // true, the same (for hex-value and boolean) -> 0 == 0
""== false, // true,
" trn "== false
);
知识兔td>

在这种情况下推荐使用‘===’或者显示的toBoolean转换。

1
2
3
4
5
6
知识兔td>
console.log(
!"0", // false
!"0x0", // false
"0"=== false// false
// etc.
);
知识兔td>

注意,null和undefined在使用‘==’时也是不等于false的。还是因为toNumber而不是toBoolean(当然了更不会和false严格相等了、即’===‘)

If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).

然后返回false。

1
2
3
4
知识兔td>
console.log(
null== false, // false, because null != ToNumber(false)< 大专栏  【译】等运算符/span>
undefined == false, // false, because undefined != ToNumber(false)
);
知识兔td>

Boolean false object is true

一些比较狗血的情况,当你new了一个object对象并传入false当做初始化参数,注意了,当使用Boolean函数或者是’!‘操作符都不会触发显式的类型转换。因为对于对象的toBoolean永远返回true。
栗子:

1
2
3
4
5
6
7
8
9
知识兔td>
var bool = newBoolean(false);
var data = !bool ? "false": "true"; // ToBoolean won't help
console.log(data); // "true"
data = bool === false? "false": "true";
console.log(data); // "true"
console.log(
Boolean(bool),
!!bool // true
);
知识兔td>

这种情况下使用’==‘会导致toNumber

1
2
知识兔td>
data = bool == false? "false": "true";
console.log(data); // "false"
知识兔td>

你也可以试试valueOf

1
知识兔td>
console.log(bool.valueOf() === false); // OK, false, as with ==
知识兔td>

手动类型强制转换

另外,你可以手动类型转换来比较string,number和boolean型。

1
2
3
4
5
6
7
知识兔td>
""+ a == ""+ b // comparing strings
+a == +b // comparing numbers
!a == !b // comparing booleans
// the same, but more human-read versions
String(a) == String(b)
Number(a) == Number(b)
Boolean(a) == Boolean(b)
知识兔td>

’+‘操作符调用操作数的valueOf方法,而’String‘调用toString方法。

1
2
3
4
5
6
知识兔td>
var foo = {
toString: function() { return"toString"; },
valueOf: function() { return"valueOf"; }
};
console.log(""+ foo); // "valueOf"
console.log(String(foo)); // "toString"
知识兔td>

尽管用’+‘连接的是“”这种空字符串,但最后的结果仍旧可以得到一个string类型。

总是显式的使用‘===’

之前已经提到过,避免使用‘==’,而应当只使用‘===’。因此你必须亲自来处理所有的类型转换。比如说:

1
2
3
知识兔td>
if(a === 1 || a.toString() === "1"|| a === true) {
...
}
知识兔td>

但如果用‘==’的话,会很简短:

1
2
3
知识兔td>
if(a == 1) {
...
}
知识兔td>

对于许多程序员来说,”显式优于隐式“这种思路是赞且灵活方便的。所以在编写ECMAScript代码中应当被接受并使用。

‘==’的安全使用情况

注意,还是有不少情况下‘===’并非必要,而且不会给代码带来任何鲁棒性。在使用ECMA-262规范下的标准行为及算法时,有些情况是完全安全的。举个被广泛使用但却无用的栗子:

1
2
3
知识兔td>
if(typeoffoo === "string") {
...
}
知识兔td>

有一件主要的事情你应当知道,那就是当操作数的类型是相同的情况下,‘==’和‘===’的处理机制是完全相同的。曾经有过一个测试 quiz question 。如果你对这个问题有兴趣的话可以查看ES3和ES5规范的相关章节。 11.9.3 The Abstract Equality Comparison Algorithm11.9.6 The Strict Equality Comparison Algorithm.

总结

好吧,如果不打算完全不用‘==’的话,那对使用‘==’和‘===’有什么一般建议呢?

还是像刚才说的,当你不确定一个函数的返回值或者一个比较变量的值,同时又要考虑类型或者是一个对象的特性,那就使用‘===’。而在其他情况下,包括完全安全的一些应用场景,比如typeof的运算结果去和string比较,这时‘==’就足够。

现在还是有一些类库(太坏了,就是在说jquery和prototype)在使用严格相等去比较typeof以及其他类似的可以完全用‘==’代替并且安全的结构。当然了也有类库(dojo,mootools,sencha)意识到了这一点而不这么做。

在一些细节优化上吹毛求疵的话,‘==’在比较相同类型的两个值时比‘===’来的更快。然而在现在许多的实现当中,这点差距实在是微不足道,甚至可以无视。此外,在其他一些实现中比较相等类型值‘===’甚至更快。在比较不同类型时,显然是‘===’更快。

当然了,一个系统的正常运行才是更关键的。所以,就像刚才说的,当你不确定一些操作或是函数的返回类型、但又不得不去考虑他们的时候,使用‘===’吧。客观的讲,我们可以想象一个程序员在某些情况下会不记得一些完全安全的‘==’用法比如typeof,所以他可能就选择‘===’来当做唯一的比较相等的操作符,这样就不用被‘==’的那些狗血尿性所扰,而专注于其他更重要的事情上去。

原文:http://dmitrysoshnikov.com/notes/note-2-ecmascript-equality-operators/

计算机