【东扯西拉】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 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. |
由此,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 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。另外 toPrimitive 和 toNumber 是内部方法,按以下规则,进行转换。
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 Otherwise if 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) | 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 |
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里面相关术语害怕解释错了,误了大家,需要多方面求证。实在是累。所以,难免会有错误。如果有疏漏,接受各方面批评和指正!