13 April 2013
(原创翻译)JS中的判断为真与相等(Truth, Equalit and JavaScript)
以前也碰到过几次 if(x)
的时候出现预想外的错误,也翻看过许多相关资料,但是一直印象不深。
今天又碰到 if({})
为true的问题,找到一篇深入介绍JS中判断相等判断true原理的文章,正好闲来无事,就动手翻译一下吧。一是加深印象,而是练练英语。翻译的不好,大家将就看哈,实在觉得我的表达看不懂的可以参考原文!
### 译文开始 ———-
哪怕你已经不是一个JS菜鸟了,你可能还是容易被下面的代码所迷惑
if ([0]) {
console.log([0] == true); //false
console.log(!![0]); //true
}
或者
if ("potato") {
console.log("potato" == false); //false
console.log("potato" == true); //false
}
好消息是,有一个所有浏览器都遵循的判断标准。一些人可能会告诉你要害怕强制类型转换,并且应该尽量避免使用它们。但是我希望说服你,强制类型转换是一种杠杆(或者理解为双刃剑?),你要做到的不是避免使用它们,你至少应该去理解它们。
x是true吗? x==y吗?在JS中判断为真或者判断相等的问题,主要出现在这三种情况下:
-
条件语句和操作符(if,三元运算符,&&, 等) - 相等判断符
==
- 严格相等判断符
===
下面就让我们看看在这三个方面中各自会发生什么吧…
条件语句
在JS中,所有条件语句和操作符都遵循相同的规则。我们将用IF语句来做范例。
在IF语句中,它将利用一个抽象方法ToBoolean,强制将判断条件的结果转换为一个boolean类型的结果。由ES5 定义的ToBoolean方法的算法为:
Argument Type | Result |
Undefined | false |
Null | false |
Boolean | The result equals the input argument (no conversion). |
Number |
The result is false if the argument is +0, −0, or NaN; otherwise the result is true. |
String |
The result is false if the argument is the empty String (its length is zero); otherwise the result is true. |
Object | true. |
也就是说,在JS中,判断为true的有(true,”potato”,36,[1,2,4] and {a:16})。
判断为false的有(false,0,”“,null and undefined)。
现在我们知道了,在我们最开始的例子中,if([0])
,一个数组是一个对象,一个对象会被强制转为true。
下面是一些补充例子,对于一些结果可能会让你惊讶,不过它们都是遵循上面说到的规则的:
var trutheyTester = function(expr) {
return expr ? "truthey" : "falsey";
}
trutheyTester({}); //truthey (an object is always true)
trutheyTester(false); //falsey
trutheyTester(new Boolean(false)); //truthey (an object!)
trutheyTester(""); //falsey
trutheyTester(new String("")); //truthey (an object!)
trutheyTester(NaN); //falsey
trutheyTester(new Number(NaN)); //truthey (an object!)
判断相等操作符(==)
用 ==
用来判断相等比较宽容。
两个值会被判断为相等,哪怕它们不是相同的类型,因为操作符会在比较前强制将其中一个或者两个值转换为相同类型(通常是number类型)。
许多开发者认为这是一件相当可怕的事情,至少一位出名的JSer毫无疑问的推荐彻底的避免使用 ==
操作符。
这种回避的策略让我很困扰,因为你在没有完全由内自外的理解一门语言之前,你都没法完全的掌控它,而恐惧与逃避是知识的天敌。
另外,假装 ==
不存在并不能解决问题,因为强制类型转换在JS中比比皆是!
它在条件语句中有出现(前面展示过的),它在数组的索引中有出现,还有许多。
而且,当强制转换被安全的运用,它能让你的代码看起来更简洁,优雅,更有可读性。
好了,总而言之,下面我们来看看ECMA是怎么定义 ==
工作的。
它真的没有那么的恐怖,只要记住的是 undefined
和 Null
能相互相等(并与其他任何都不相等),还有大部分类型都被强制转换为number类型以加速判断了:
Type(x) | Type(y) | Result |
x and y are the same type | See Strict Equality (===) Algorithm | |
null | Undefined | true |
Undefined | null | true |
Number | String | x == toNumber(y) |
String | Number | toNumber(x) == y |
Boolean | (any) | toNumber(x) == y |
(any) | Boolean | x == toNumber(y) |
String or Number | Object | x == toPrimitive(y) |
Object | String or Number | toPrimitive(x) == y |
otherwise… | false |
如果算法的结果是一个表达式,它将被重新申请计算,直到结果是一个boolean值。
toNumber
和 toPrimitive
是内置方法,它将会把输入值按照下面规则转换:
Argument Type | Result |
Undefined | NaN |
Null | +0 |
Boolean |
The result is 1 if the argument is true. The result is +0 if the argument is false. |
Number | The result equals the input argument (no conversion). |
String |
In effect evaluates Number(string) “abc” -> NaN “123″ -> 123 |
Object |
Apply the following steps:
1. Let primValue be ToPrimitive(input argument, hint Number). |
Argument Type | Result |
Object |
(in the case of equality operator coercion) if valueOf returns a primitive, return it. Otherwise if toString returns a primitive return it. Otherwise throw an error |
otherwise… | The result equals the input argument (no conversion). |
下面是一些例子,我将会用模拟算法来一步步展示强制转换的算法是怎么工作的:
[0] == true;
//EQUALITY CHECK...
[0] == true;
//HOW IT WORKS...
//convert boolean using toNumber
[0] == 1;
//convert object using toPrimitive
//[0].valueOf() is not a primitive so use...
//[0].toString() -> "0"
"0" == 1;
//convert string using toNumber
0 == 1; //false!
“potato” == true;
//EQUALITY CHECK...
"potato" == true;
//HOW IT WORKS...
//convert boolean using toNumber
"potato" == 1;
//convert string using toNumber
NaN == 1; //false!
“potato” == false;
//EQUALITY CHECK...
"potato" == false;
//HOW IT WORKS...
//convert boolean using toNumber
"potato" == 0;
//convert string using toNumber
NaN == 0; //false!
object with valueOf
//EQUALITY CHECK...
crazyNumeric = new Number(1);
crazyNumeric.toString = function() {return "2"};
crazyNumeric == 1;
//HOW IT WORKS...
//convert object using toPrimitive
//valueOf returns a primitive so use it
1 == 1; //true!
object with toString
//EQUALITY CHECK...
var crazyObj = {
toString: function() {return "2"}
}
crazyObj == 1;
//HOW IT WORKS...
//convert object using toPrimitive
//valueOf returns an object so use toString
"2" == 1;
//convert string using toNumber
2 == 1; //false!
严格模式相等操作符===
这个就简单多了。当两值类型不同时,总是得到false。如果它们是相同类型,那么凭直觉测试得到相等的情况:对象必须指向相同的对象,字符串必须包含相同的字符,其他原始类型必须拥有相同的值。
NaN,null和undefined永远不会===其他类型,而NaN甚至===自己都为false。
Type(x) | Values | Result |
Type(x) different from Type(y) | false | |
Undefined or Null | true | |
Number |
x same value as y (but not NaN ) |
true |
String | x and y are identical characters | true |
Boolean | x and y are both true or both false | true |
Object | x and y reference same object | true |
otherwise… | false |
一些过分使用严格匹配的常见的例子:
(2015.5.10补充:使用严格相等可以略微加快运算效率,在确定类型的情况下推荐使用===)
//unnecessary
if (typeof myVar === "function");
//better
if (typeof myVar == "function");
因为typeof返回的是一个字符串,这个操作将会一直是比较两个字符串,所以==是100%coercion-proof(不知道怎么翻,大概是100%严格匹配?)
//unnecessary
var missing = (myVar === undefined || myVar === null);
//better
var missing = (myVar == null);
null和undefined是相互==的
提示:因为有(非常小)的出错可能,undefined变量被重新定义了,所以与null比较会相对安全些。
//unnecessary
if (myArray.length === 3) {//..}
//better
if (myArray.length == 3) {//..}
Posted in 2013-04-13 20:33