【东扯西拉】Javascript 真假 等于 判断 -- 从标准看Javascript 类型转换

每篇推荐:

篇头资源分享:

javascript中的对象查找

前端开发理论热点面对面:从怎么看,到怎么做?

正文:

一般初学js时,对下面两段代码都很困惑:

1 if ([0]) {
2 console.log([0] == true); //false
3 console.log(!![0]); //true
4 }
1 if ("potato") {
2 console.log("potato" == false); //false
3 console.log("potato" == true); //false
4 }

真假性判断 ,字面意思就是,x 是真?

等性判断,字面意思就是,x等于y?

在js里,主要有3个方面会有这种判断:条件语句 和 操作符(if,?,&&,|| 等),等于判断(==),严格等于判断(===)。

下面分别描述:

条件语句:

所有条件语句和操作符都遵循相同的转换规则。就以if 语句为例:

在 if ( Expression ) Statement 结构,执行过程为:拿(执行)表达式后的结果,使用内部方法 ToBoolean ,将其转换为boolean。在es5 spec是这样定义ToBoolean的:

Argument TypeResult
Undefinedfalse
Nullfalse
BooleanThe result equals the input argument (no conversion).
NumberThe result is false if the argument is +0, −0, or NaN;

otherwise the result is true.

StringThe result is false if the argument is the empty String (its length is zero);

otherwise the result is true.

Objecttrue.

由此,true, "photo", 36, [1,2,4], {a:12} 是真 ;false, 0, "", null , undefined 为假。

回到上面那段代码,if([0]) 应该明白为什么为true了吧。因为 数组是一个对象,所有对象都是true。

再看看下面例子,试着按照上表,测验一下自己有感觉了没。

 1 var trutheyTester = function(expr) {
2 return expr ? "truthey" : "falsey";
3 }
4
5 trutheyTester({}); //truthey (an object is always true)
6
7 trutheyTester(false); //falsey
8 trutheyTester(new Boolean(false)); //truthey (an object!)
9
10 trutheyTester(""); //falsey
11 trutheyTester(new String("")); //truthey (an object!)
12
13 trutheyTester(NaN); //falsey
14 trutheyTester(new Number(NaN)); //truthey (an object!)

等于运算:

判断 x == y时, 如果是相同类型,直接比较,如果不同类型,会把x,y转换为一种类型(通常是number),再执行比较。由于js这种自动类型转换特性,一些jser会要求避免使用 == 判断。

但是,就算不用 == ,难道我们就能完全避免自动类型转换了吗?记住:js自动类型转换 无处不在!你会发现它在if 语句里,数组索引里,连接类的运算里 等等。所以,从原理上掌握这种具体实现细节,才能从根本上 解决问题,灵活运用。

对于等于运算 ,标准里是这样定义的(如下表)。请记住:undefined == null , undefined == undefined ,null == null 和 除此之外,其他类型与undefined或者null 比较 都是false。另外,其他类型都会转换为number再比较(例如true == 1 ==> 1 ==1 ==>true

Type(x)Type(y)Result
x and y are the same typeSee Strict Equality (===) Algorithm
nullUndefinedtrue
Undefinednulltrue
NumberStringx == toNumber(y)
StringNumbertoNumber(x) == y
Boolean(any)toNumber(x) == y
(any)Booleanx == toNumber(y)
String or NumberObjectx == toPrimitive(y)
ObjectString or NumbertoPrimitive(x) == y
otherwise…false

当结果还是一个表达式时,会继续执行上表规则,直到结果是boolean。另外 toPrimitivetoNumber 是内部方法,按以下规则,进行转换。

ToNumber
Argument TypeResult
UndefinedNaN
Null+0
BooleanThe result is 1 if the argument is true.

The result is +0 if the argument is false.

NumberThe result equals the input argument (no conversion).
StringIn effect evaluates Number(string)

“abc” -> NaN

“123″ -> 123

ObjectApply the following steps:

1. Let primValue be ToPrimitive(input argument, hint Number).

2. Return ToNumber(primValue).

ToPrimitive
Argument TypeResult
Object

(in the case of equality operator coercion)

if valueOf returns a primitive, return it. [1]

Otherwise if toString returns a primitive return it. [2]

Otherwise throw an error

otherwise…The result equals the input argument (no conversion).

在ToPrimitive表中,[1] 和[2] 的顺序 不能颠倒!

下面一些例子,让我们来看js引擎是怎么一步步执行的:

[0] == true

 1 //EQUALITY CHECK...
2 [0] == true;
3
4 //HOW IT WORKS...
5 //convert boolean using toNumber
6 [0] == 1;
7 //convert object using toPrimitive
8 //[0].valueOf() is not a primitive so use...
9 //[0].toString() -> "0"
10 "0" == 1;
11 //convert string using toNumber
12 0 == 1; //false!

"hello" == true

1 //EQUALITY CHECK...
2 "hello" == true;
3
4 //HOW IT WORKS...
5 //convert boolean using toNumber
6 "hello" == 1;
7 //convert string using toNumber
8 NaN == 1; //false!

"hello" == false

1 //EQUALITY CHECK...
2 "hello" == false;
3
4 //HOW IT WORKS...
5 //convert boolean using toNumber
6 "hello" == 0;
7 //convert string using toNumber
8 NaN == 0; //false!

针对对象:

所有对象都是不相等的,除非是同一个变量引用

1 var o={};
2 console.log(o==o);//true
3 console.log(o=={});//false
4
5 var a={};
6 console.log(a==o) ;//false
7
8 var b={};
9 console.log(a==b) ;//false

使用内置对象构造函数创建对象

1 //EQUALITY CHECK...
2 crazyNumeric = new Number(1);
3 crazyNumeric.toString = function() {return "2"};
4 crazyNumeric == 1;
5
6 //HOW IT WORKS...
7 //convert object using toPrimitive
8 //valueOf returns a primitive so use it
9 1 == 1; //true!

对象字面量 创建对象

 1 //EQUALITY CHECK...
2 var crazyObj = {
3 toString: function() {return "2"}
4 }
5 crazyObj == 1;
6
7 //HOW IT WORKS...
8 //convert object using toPrimitive
9 //valueOf returns an object so use toString
10 "2" == 1;
11 //convert string using toNumber
12 2 == 1; //false!

可以看到,上面2个例子,都重写了toString方法,那么,如果重写valueOf 结果会怎么样?

 1 var one = {
2 valueOf: function () {
3 return 1;
4 },
5 toString: function () {
6 return "2";
7 }
8 };
9
10 console.log(one == 1); // true
11 console.log(one == "2"); // false

如果移除valueOf,结果又会怎样?

var one = {
toString: function () {
return "2";
}
};
console.log(one == 1); // fales
console.log(one == "2"); // true
//use valueOf return an object ,then use toString .So return true!

严格等于运算(===):

由于没有类型转换,所以比较起来,相对比较简单。

如果操作数是不同类型的,返回false。如果是相同类型,下列情况返回true:对象标识符必须引用同一对象,字符串必须含有相同字符,其它原始类型必须是相同的值。NaN,null,undefined 永远不严格等于 其他类型。NaN也不严格等于自身。

Type(x)ValuesResult
Type(x) different from Type(y)false
Undefined or Nulltrue
Numberx same value as y (but not NaN)true
Stringx and y are identical characterstrue
Booleanx and y are both true or both falsetrue
Objectx and y reference same objecttrue
otherwise…false

没有必要的判断:

1 //unnecessary
2 if (typeof myVar === "function");
3
4 //better
5 if (typeof myVar == "function");

由于typeof 返回一个 string 值,两边都是已经很明确的类型。所以不需要使用严格等于。

1 //unnecessary
2 var missing = (myVar === undefined || myVar === null);
3
4 //better
5 var missing = (myVar == null);

由于undefined == null , null == null ,undefined == undefined ,所以下面是等价的。有时候,可能 undefined 会出现重新定义的情况,让其 ==null 会更安全一点。

1 //unnecessary
2 if (myArray.length === 3) {//..}
3
4 //better
5 if (myArray.length == 3) {//..}

参考资料:

1. Truth, Equality and JavaScript

2.JavaScript Coercion Demystified

ECMA-262 5th Edition

11.9.3 The Abstract Equality Comparison Algorithm

11.9.6 The Strict Equality Comparison Algorithm

  9.1 toPrimitive

  9.2 toBoolean

  9.3 toNumber

补充说明:

从早上一直写,写了一天,由于本身水平有限,一些相关描述可能会有疏漏,js里面相关术语害怕解释错了,误了大家,需要多方面求证。实在是累。所以,难免会有错误。如果有疏漏,接受各方面批评和指正!