前言
JavaScript 的 == 和 === 两种判断相等的方法行为不一致,很让人头晕,而且还有 typeof 和 instanceof所以记录一下,解决这个问题
== 、 === 和 Object.is()
==
双等号 == ,也叫 宽松相等,相等操作符比较两个值是否相等,在比较前将两个被比较的值转换为相同类型。在转换后(等式的一边或两边都可能被转换),最终的比较方式等同于全等操作符 === 的比较方式。 相等操作符满足交换律。
经典例子
1  | var num = 0;  | 
内部原理
标准的相等性操作符(== 与 !=)使用了Abstract Equality Comparison Algorithm来比较操作符两侧的操作对象(x == y),该算法流程要点提取如下:
- 如果 x 或 y 中有一个为 
NaN,则返回false; - 如果 x 与 y 皆为 
null或undefined中的一种类型,则返回true(null == undefined // true);否则返回false(null == 0 // false); - 如果 x,y 类型不一致,且 x,y 为 
String、Number、Boolean中的某一类型,则将 x,y 使用toNumber函数转化为Number类型再进行比较; - 如果 x,y 中有一个为 
Object,则首先使用ToPrimitive函数将其转化为原始类型,再进行比较。 
1  | ToPrimitive(obj,preferredType)  | 
还有一种情况:1
console.log([]==![]);// true
具体涉及到了运算符优先级,可以看看这篇文章,讲的非常详细
===
三等号 ===,叫 严格相等比较,三等号将进行相同的比较,而不进行类型转换 (如果类型不同, 只是总会返回 false )
经典例子
用 === 就不会出现 == 的一些奇怪的结果1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var num = 0;
var obj = new String("0");
var str = "0";
var b = false;
console.log(num === num); // true
console.log(obj === obj); // true
console.log(str === str); // true
console.log(num === obj); // false
console.log(num === str); // false
console.log(obj === str); // false
console.log(null === undefined); // false
console.log(obj === null); // false
console.log(obj === undefined); // false
在日常中使用全等操作符几乎总是正确的选择,但还是有几个特殊的例子1
2
3
4console.log(NaN === NaN);// false
console.log(Symbol(1) === Symbol(1));// false
console.log({x:1}==={x:1});// false
console.log([]===[]);// false
对于 NaN ,是 IEEE 754 规范上写的,而 Symbol 是 ES6 新增的原始数据类型,代表的意思就是独一无二的,所以当然不会相等,{x:1} 是一个对象,即使再声明一个一样的对象,它的内存地址不一样,也当然不一样了,[] 也是同样的道理,内存地址不同运用在 == 也是一样的。
Object.is()
由于 === 也会出现一些奇怪的判断,所以在 ES6 加入了 Object.is() 对于 NaN和 -0 和 +0 进行特殊处理。1
console.log(Object.is(NaN,NaN));// true
全部比较
                typeof 和 instanceof
另外两个比较混乱的判断就是 typeof 和 instanceof
typeof
typeof 操作符返回一个字符串,表示未经计算的操作数的类型。
经典例子
1  | // Numbers  | 
typeof null
还有一个特别的例子:1
typeof null === 'object';
官方解释是:
在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null的类型标签也成为了 0,typeof null就错误的返回了”object”。
new 操作符
1  | // 所有的构造函数在实例化一个对象的时候 typeof 都会返回 'object'  | 
instanceof
有时候我们需要判断一个对象的类型,如果我们用 typeof 只会得到 "object":1
2
3
4
5var arr = [1,2,3,4]
typeof arr;// "object"
var date = new Date();
typeof date;// "object"
这个时候就要用 instanceof 。instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。1
2
3
4
5var arr = [1,2,3,4]
arr instanceof Array// true
var date = new Date();
date instanceof Date// true
但这并不是 instanceof 原来的用法,instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 定义构造函数
function C(){} 
function D(){} 
var o = new C();
o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype
o instanceof D; // false,因为 D.prototype不在o的原型链上
o instanceof Object; // true,因为Object.prototype.isPrototypeOf(o)返回true
C.prototype instanceof Object // true,同上
C.prototype = {};
var o2 = new C();
o2 instanceof C; // true
o instanceof C; // false,C.prototype指向了一个空对象,这个空对象不在o的原型链上.
D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true
需要注意的是,如果表达式 obj instanceof Foo 返回 true,则并不意味着该表达式会永远返回 true,因为 Foo.prototype 属性的值有可能会改变,改变之后的值很有可能不存在于 obj 的原型链上,这时原表达式的值就会成为 false 。另外一种情况下,原表达式的值也会改变,就是改变对象 obj 的原型链的情况,虽然在目前的 ES 规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的__proto__魔法属性,是可以实现的。比如执行 obj.__proto__ = {} 之后,obj instanceof Foo 就会返回 false 了。
神奇的例子
1  | var simpleStr = "This is a simple string";  | 
所以我们判断是否是数组还是用这种方法Object.prototype.toString.call(myObj) === "[object Array]" 或者 ES6 新增的 Array.isArray(myObj)。