即便你不是一个JavaScript的新手也会被下面这段代码搞糊涂
if ([0]) {
console.log([0] == true); //false
console.log(!![0]); //true
}
或者像这样的
if ("potato") {
console.log("potato" == false); //false
console.log("potato" == true); //false
}
好消息是,我们有一个所有浏览器都遵循的标准。有些作者会告诉你要小心强制 转换,并且在编码的时候要避免这种情况。我希望能说服你,强换是一种可被利用的特点(或者说在最低可被理解的条件下),而不是避免。
x的值为真吗?x和y是否相等?真值和相等判断是JavaScript三个主要领域的核心:条件语句和操作符(if,三目,&&,||等等……),相等操作符( == ),以及强等于操作符( === )。让我们看看再每种情况下会发生什么……
条件语句
在JavaScript中,所有的条件语句和操作符遵循同样的强制转换模式。我们将用if
语句作为例子。
if
(表达式)语句结构将会使用抽象方法ToBoolean把表达式的运算结果强转成一个布尔值。为了实现这个抽象方法,ES5定义了如下算法:
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. |
这是javascript用来把值分为真值(true
,“potato”
,36
,[1,2,3]
以及{a:16}
)和假值(false
,0
,""
,null
以及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!)
相同操作符(==)
判等的 ==
版本是相当自由的。即使它们是不同的类型,值也可以被认为是相等的,因为操作符在进行比较之前,会强制将一个或两个操作数强制转换为单个类型(通常是一个数字)。许多开发人员发现这有点吓人,毫无疑问,至少有一个著名的JavaScript专家建议完全避免使用==
操作符。
回避策略困扰着我,因为在你完全了解一门语言之前,你无法掌握它——恐惧和逃避是知识的敌人。此外,假装 == 不存在不会让你在理解强制转换时摆脱困境,因为JavaScript中强制转换无处不在!它在条件表达式中(正如我们刚才看到的),它在数组索引中,它在连接符中等等。此外,如果安全地使用,强制转换可以成为编写简洁、优雅以及易读代码的工具。
不管怎样,我们来看看ECMA是如何定义 == 的。它真的没有那么吓人。只要记住,undefined
和null
是相等的(没有别的),大多数其他类型都被强制转换成一个数字来进行比较:
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 |
在结果是表达式的地方,算法被重新应用,直到结果是布尔值。toNumber 和 toPrimitive是内部方法,用来根据下面的规则进行转换:
参数类型 | 结果 |
Undefined | NaN |
Null | +0 |
Boolean | 如果参数为 true,结果 1 。 如果参数为 false,结果 +0。 |
Number | 结果即为输入的参数 (不转换)。 |
String | Number(string) 的实际计算结果 “abc” -> NaN “123” -> 123 |
Object | 应用下面的步骤: 1. Let primValue be ToPrimitive(input argument, hint Number). |
Argument Type | Result |
Object | (在相等运算符强转的条件下) 如果valueOf 返回一个基本类型, 那么返回它。 否则,如果 toString 返回的是基本类型,那么返回 toString 的值。如果都不是,抛出异常。 |
otherwise… | 结果等于输入的参数 (不转换)。 |
这里有一些例子,我将使用伪代码一步一步地演示如何应用强制转换算法:
[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
甚至不会 === 自己。
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 |
过度使用强等判断的一些例子
//unnecessary
if (typeof myVar === "function");
//better
if (typeof myVar == "function");
由于typeOf
返回一个字符串,所以这个操作总是比较两个字符串。因此== 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) {//..}
前面说的已经够多了。
原文链接:https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/