JS 深度比较 与 `===`

可以回顾何时使用==何时使用===

全等比较(严格相等)

  • 当我们使用===比较引用类型时,是会对地址进行比较的,所以就算属性和属性值完全相等的两个对象也是不等的。

深度比较

  • 深度比较则不会对引用类型的地址进行比较,只要内容相等就判定为相等。
  • 可以使用**lodash库的isEqual()**来进行深度比较,也可以自己使用js写
  • 回顾:lodash库的cloneDeep()可以用来深拷贝引用类型
  • 注意:lodash库的 isEqual()深度比较 和 cloneDeep()深拷贝 都很耗费性能,他们都要进行一次性递归到底。轻易不推荐使用。

js实现深度比较

思路:

  1. 先判断2个对象的数据类型是否一致。
  2. 如果对象的数据是基础的数据类型则直接比较; 如果是 Number, 对NaN进行特殊处理。
  3. 如果对象的数据类型是 Array,对象进行循环, 逐值进行判断。
  4. 如果对象的数据类型是 Object,分别对象的key, value 进行判断。(Reflect.ownKeys() 返回一个由目标对象自身的属性键组成的数组)
  5. 如果对象的数据类型是 Map 或者 Set, 转化为Array进行判断。

代码:

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
/**
*
* 返回对相应的数据类型
*/
function getType(data) {
return Object.prototype.toString.call(data).substring(8).split(/]/)[0]
}

// 假设data为123
// Object.prototype.toString.call(data)得到"[object Number]"
// substring从第八位开始截取,得到"Number]"
// split从]分隔为数组,取第一位,得到Number
// 注意:NaN也会得到Number,但NaN.toString()可得NaN

/**
*
* @param {*} sourceObj
* @param {*} compareObj
*
* 比较对象是否相等
*
*/
function comparisonObject(sourceObj, compareObj) {
if (arguments.length < 2) throw "Incorrect number of parameters";
let sourceType = getType(sourceObj);
if (sourceType !== getType(compareObj)) return false;
// Not objects and arrays
if (sourceType !== "Array" && sourceType !== "Object" && sourceType !== "Set" && sourceType !== "Map") {
if (sourceType === "Number" && sourceObj.toString() === "NaN") {
return compareObj.toString() === "NaN"
}
if (sourceType === "Date" || sourceType === "RegExp") {
return sourceObj.toString() === compareObj.toString()
}
return sourceObj === compareObj
} else if (sourceType === "Array") {
if (sourceObj.length !== compareObj.length) return false;
if (sourceObj.length === 0) return true;//???
for (let i = 0; i < sourceObj.length; i++) {
if (!comparisonObject(sourceObj[i], compareObj[i])) return false;
}
} else if (sourceType === "Object") {
// 通过Reflect.ownKeys得到属性名组成的数组
let sourceKeyList = Reflect.ownKeys(sourceObj);
let compareKeyList = Reflect.ownKeys(compareObj);
let key;
if (sourceKeyList.length !== compareKeyList.length) return false;
for (let i = 0; i < sourceKeyList.length; i++) {
key = sourceKeyList[i];
// 比较属性名
if (key !== compareKeyList[i]) return false;
// 比较属性值
if (!comparisonObject(sourceObj[key], compareObj[key])) return false;
}
} else if (sourceType === "Set" || sourceType === "Map") {
// 把 Set Map 转为 Array
if (!comparisonObject(Array.from(sourceObj), Array.from(compareObj))) return false;
}
return true;
}
,