JS 深比较相等
首先来看几个例子:
console.log(0 === 0); // true
console.log('str' === 'str'); // true
console.log(true === true); // true
console.log(undefined === undefined); // true
console.log(null === null); // true
console.log({} === {}); // false
console.log({val: 1} === {val: 1}); // true
console.log([1,2,3] === [1,2,3]); // true
console.log([1,2,{a: 1}] === [1,2,{a: 1}]); // true
基本类型使用 === 都可以判断相等,而引用类型由于存放于栈中的仅仅是地址,因此看上去一模一样的对象或者数组却不相等,可以看出,=== 仅仅是浅比较。
JS 中几种比较算法可以查阅 ES 标准中的相等比较算法
那么如何实现深比较呢,直接上代码:
function deepEquals (val1, val2) {
// NaN 对比情况判断为相同
if (Number.isNaN(val1) && Number.isNaN(val2)) return true;
// 是否为对象
let valIsObject = function (val) {
return typeof val === 'object' && val !== null;
}
if (!valIsObject(val1) || !valIsObject(val2)) return val1 === val2;
// 当val1和val2都为对象时,若地址相同则相等
if (val1 === val2) return true;
// 到这一步可以判断 val1 和 val2 都是对象,为了区分 {} 和 [],如果没有此判断,会导致 deepEquals({}, []) 返回 true
let isEmptyObj = (Array.isArray(val1) && !Array.isArray(val2)) || (Array.isArray(val2) && !Array.isArray(val1));
if (isEmptyObj) return false;
// 获取键的长度,长度不等则不同,也是为了下面遍历时以短的为基准
if (Object.keys(val1).length !== Object.keys(val2).length) return false;
for (let key in val1) {
if (val1.hasOwnProperty(key)) {
const isEqual = deepEquals(val1[key], val2[key])
if (!isEqual) return isEqual;
}
}
return true;
}
通过剪枝提高了执行效率
现在使用该深比较函数 deepEquals 来判断下之前的类型:
console.log(deepEquals(0, 0)); // true
console.log(deepEquals('str', 'str')); // true
console.log(deepEquals(true, true)); // true
console.log(deepEquals(undefined, undefined)); // true
console.log(deepEquals(null, null)); // true
console.log(deepEquals({}, {})); // true
console.log(deepEquals({val: 1}, {val: 1})); // true
console.log(deepEquals([1,2,3], [1,2,3])); // true
console.log(deepEquals([1,2,{a: 1}], [1,2,{a: 1}])); // true
可以看到,通过自定义 deepEquals 函数实现了引用类型的深比较
通过深比较,可以辅助实现数组中引用类型去重,相关内容可查阅 JS 数组去重