判断两个数组是否相等
`===` 严格相等,会比较两个值的类型和值
`==` 抽象相等,比较时,会先进行类型转换,然后再比较值
然后我就更迷惑了,先类型转换,我擦,怎么转换,左边转换成右边类型还是右边转换成左边类型?
先看几个例子:
console.log([10] == 10); //true console.log('10' == 10); //true console.log([] == 0); //true console.log(true == 1); //true console.log([] == false); //true console.log(![] == false); //true console.log('' == 0); //true console.log('' == false); //true console.log(null == false); //false console.log(!null == true); //true console.log(null == undefined); //true
严格运算符===的运算规则如下,
(1)不同类型值
如果两个值的类型不同,直接返回false。
(2)同一类的原始类型值
同一类型的原始类型的值(数值number、字符串string、布尔值boolean)比较时,值相同就返回true,值不同就返回false。
(3)同一类的复合类型值/高级类型
两个复合类型(对象Object、数组Array、函数Funtion)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个对象。即“地址指针”是否相等。
(4)undefined和null
//undefined 和 null 与严格不相等。
null === null //true
undefined === undefined //true
undefined === null //false
三、相等运算符 "== "的运算规则
相等运算符"=="在比较相同类型的数据时,与严格运算符"==="完全一样。
在比较不同类型的数据时,相等运算符"=="会先将数据进行类型转换,然后再用严格相等运算符"==="比较。类型转换规则如下:
(1)原始类型的值
原始类型的数据会转换成数值类型再进行比较。字符串和布尔值都会转换成数值。
(2)对象与原始类型值比较
对象(这里指广义的对象,包括数值和函数)与原始类型的值比较时,对象转化成原始类型的值,再进行比较。
(3)undefined和null
undefined和null与其他类型的值比较时,结果都为false,它们互相比较时结果为true。
(4)相等运算符"=="的缺点
相等运算符"=="隐藏的类型转换,会带来一些违反直觉的结果。
'' == '0' // false 0 == '' // true 0 == '0' // true false == 'false' // false false == '0' // true false == undefined // false false == null // false null == undefined // true
==和=== 判断数组问题 JavaScript里面Array是对象,==或===操作符只能比较两个对象是否是同一个实例,也就是是否是同一个对象引用。
判断对象是否相等
在Javascript中相等运算包括"==","==="全等,两者不同之处,不必多数,本篇文章我们将来讲述如何判断两个对象是否相等? 你可能会认为,如果两个对象有相同的属性,以及它们的属性有相同的值,那么这两个对象就相等。那么下面我们通过一个实例来论证下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
var obj1 = { name: "Benjamin" , sex : "male" } var obj2 = { name: "Benjamin" , sex : "male" } //Outputs: false console.log(obj1 == obj2); //Outputs: false console.log(obj1 === obj2); |
通过上面的例子可以看到,无论使用"=="还是"===",都返回false。主要原因是基本类型string,number通过值来比较,而对象(Date,Array)及普通对象通过指针指向的内存中的地址来做比较。看下面一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
var obj1 = { name: "Benjamin" , sex : "male" }; var obj2 = { name: "Benjamin" , sex : "male" }; var obj3 = obj1; //Outputs: true console.log(obj1 == obj3); //Outputs: true console.log(obj1 === obj3); //Outputs: false console.log(obj2 == obj3); //Outputs: false console.log(obj2 === obj3); |
上例返回true,是因为obj1和ob3的指针指向了内存中的同一个地址。和面向对象的语言(Java/C++)中值传递和引用传递的概念相似。 因为,如果你想判断两个对象是否相等,你必须清晰,你是想判断两个对象的属性是否相同,还是属性对应的值是否相同,还是怎样?如果你判断两个对象的值是否相等,可以像下面这样:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
|
function isObjectValueEqual(a, b) { // Of course, we can do it use for in // Create arrays of property names var aProps = Object.getOwnPropertyNames(a); var bProps = Object.getOwnPropertyNames(b); // If number of properties is different, // objects are not equivalent if (aProps.length != bProps.length) { return false ; } for ( var i = 0; i < aProps.length; i++) { var propName = aProps[i]; // If values of same property are not equal, // objects are not equivalent if (a[propName] !== b[propName]) { return false ; } } // If we made it this far, objects // are considered equivalent return true ; } var obj1 = { name: "Benjamin" , sex : "male" }; var obj2 = { name: "Benjamin" , sex : "male" }; //Outputs: true console.log(isObjectValueEqual(obj1, obj2)); |
正如你所看到的,检查对象的“值相等”我们基本上是要遍历的对象的每个属性,看看它们是否相等。
虽然这个简单的实现适用于我们的例子中,有很多情况下,它是不能处理。
例如:
1 如果该属性值之一本身就是一个对象吗?
2 如果属性值中的一个是NaN(在JavaScript中,是不是等于自己唯一的价值?)
3 如果一个属性的值为undefined,而另一个对象没有这个属性(因而计算结果为不确定?)
检查对象的“值相等”的一个强大的方法,最好是依靠完善的测试库,涵盖了各种边界情况。Underscore和Lo-Dash有一个名为_.isEqual()方法,用来比较好的处理深度对象的比较。您可以使用它们像这样:
1
2
|
// Outputs: true console.log(_.isEqual(obj1, obj2)); |
需要引入loDash库,loDash是一个JavaScript 的实用工具库
<script src="./loDash.js"></script>
<script>
var obj1={name:'zjx',age:23,props:{test:'test'}}
var obj2={name:'zjx',age:23,props:{test:'test'}}
console.log(_.isEqual(obj1, obj2));
</script>
最后附上Underscore中isEqual的部分源码:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
// Internal recursive comparison function for `isEqual`. var eq = function (a, b, aStack, bStack) { // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) return a !== 0 || 1 / a === 1 / b; // A strict comparison is necessary because `null == undefined`. if (a == null || b == null ) return a === b; // Unwrap any wrapped objects. if (a instanceof _) a = a._wrapped; if (b instanceof _) b = b._wrapped; // Compare `[[Class]]` names. var className = toString.call(a); if (className !== toString.call(b)) return false ; switch (className) { // Strings, numbers, regular expressions, dates, and booleans are compared by value. case '[object RegExp] ': // RegExps are coerced to strings for comparison (Note: ' ' + /a/i === ' /a/i ') case ' [object String] ': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. return ' ' + a === ' ' + b; case ' [object Number] ': // `NaN`s are equivalent, but non-reflexive. // Object(NaN) is equivalent to NaN if (+a !== +a) return +b !== +b; // An `egal` comparison is performed for other numeric values. return +a === 0 ? 1 / +a === 1 / b : +a === +b; case ' [object Date] ': case ' [object Boolean] ': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. return +a === +b; } if (typeof a != ' object ' || typeof b != ' object ') return false; // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. var length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. if (aStack[length] === a) return bStack[length] === b; } // Objects with different constructors are not equivalent, but `Object`s // from different frames are. var aCtor = a.constructor, bCtor = b.constructor; if ( aCtor !== bCtor && // Handle Object.create(x) cases ' constructor ' in a && ' constructor ' in b && !(_.isFunction(aCtor) && aCtor instanceof aCtor && _.isFunction(bCtor) && bCtor instanceof bCtor) ) { return false; } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); var size, result; // Recursively compare objects and arrays. if (className === ' [object Array]') { // Compare array lengths to determine if a deep comparison is necessary. size = a.length; result = size === b.length; if (result) { // Deep compare the contents, ignoring non-numeric properties. while (size--) { if (!(result = eq(a[size], b[size], aStack, bStack))) break ; } } } else { // Deep compare objects. var keys = _.keys(a), key; size = keys.length; // Ensure that both objects contain the same number of properties before comparing deep equality. result = _.keys(b).length === size; if (result) { while (size--) { // Deep compare each member key = keys[size]; if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break ; } } } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); return result; }; // Perform a deep comparison to check if two objects are equal. _.isEqual = function (a, b) { return eq(a, b, [], []); }; |