需求: 如果两个数组中的元素仅仅是 index 不同, 则认为两个数组是相等的; 如果两个对象中, 它们基本数据类型的数据是相同的, 它们的引用数据类型中 k-v 都是可以一一对应的, 则认为两个对象是相等的.
注意: 只考虑了数据类型中 Null, Undefined, String, Number, Object, Array 的情况, 没有考虑到一些边界情况, 比如 NaN 等, 也没有考虑 Map 和 Set 的数据类型.
对于业务中普通的 Js 对象的判断基本够用
function deepCompare(x, y) {
const typeNull = '[object Null]'
const typeUndefined = '[object Undefined]'
const typeObject = '[object Object]'
const typeArr = '[object Array]'
const myTypeof = (v) => Object.prototype.toString.call(v)
function doCompare(x, y) {
// console.log('doCompare', x, y)
/**
* 判断基本类型
*/
const typeX = myTypeof(x)
const typeY = myTypeof(y)
if (typeX !== typeY) return false
if ((typeX === typeNull && typeY === typeNull) ||
(typeX === typeUndefined && typeY === typeUndefined)
) {
return true
}
if (isNaN(x) && isNaN(y) && typeof x === 'number' && y === 'number') return true
if (x === y) return true
if ((typeof x === 'function' && typeof y === 'function') ||
(x instanceof Date && y instanceof Date) ||
(x instanceof RegExp && y instanceof RegExp) ||
(x instanceof String && y instanceof String) ||
(x instanceof Number && y instanceof Number)) {
return x.toString() === y.toString()
}
/**
* 判断数组
*/
if (Array.isArray(x) || Array.isArray(y)) {
if (x.length !== y.length) return false
if (x.length === y.length && x.length === 0) return true
for (let itemInX of x) {
const resultList = []
for (let itemInY of y) {
const result = doCompare(itemInX, itemInY)
resultList.push(result)
}
if (resultList.every(item => !item)) {
return false
}
}
for (let itemInY of y) {
const resultList = []
for (let itemInX of x) {
const result = doCompare(itemInX, itemInY)
resultList.push(result)
}
if (resultList.every(item => !item)) {
return false
}
}
return true
}
/**
* 判断对象
*/
if (myTypeof(x) === typeObject &&
myTypeof(y) === typeObject
) {
let k
const xKeys = Reflect.ownKeys(x)
const yKeys = Reflect.ownKeys(y)
if (xKeys.length === yKeys.length && xKeys.length === 0) {
return true
}
if (xKeys.length !== yKeys.length) {
return false
}
// 比较 key
for (k of xKeys) {
if (!Reflect.has(y, k)) {
return false
}
}
for (k of yKeys) {
if (!Reflect.has(x, k)) {
return false
}
}
// 比较 value
for (k of xKeys) {
switch (typeof x[k]) {
case 'object':
if (!doCompare(x[k], y[k])) {
return false
}
break
default:
if (x[k] !== y[k]) {
return false
}
break
}
}
return true
}
return false
}
const result = doCompare(x, y)
return result
}
const testData1 = {
name: 'abc',
childObj: {
label1: '123',
arr: [{ name: '123' }, { name: '1234' }, { name: '12345' }]
},
childArr: [{ name: '123' }, { name: '123' }, { name: '123456' }, { name: '123' }],
}
const testData2 = {
name: 'abc',
childArr: [{ name: '123' }, { name: '123456' }, { name: '123' }, { name: '123' }],
childObj: {
label1: '123',
arr: [{ name: '123' }, { name: '12345' }, { name: '1234' }]
}
}
const testObj1 = {
key: 'abc',
value: {
key: 'def',
value: {
key: 'efg',
value: 123
}
},
type: null
}
const testObj2 = {
key: 'abc',
value: {
key: 'defg',
value: {
key: 'efg',
value: 123
}
},
type: 0
}
const testObjWithArr1 = {
key: 'abc',
value: {
key: 'def',
value: {
key: 'efg',
value: [{id: '1234', name: 'test2'}, {id: '123', name: 'test'}]
},
value2: {
child: {
id: 'abc, def',
value: [{id: '123', name: 'test'}, {id: '1234', name: 'test2'}, {id: '1234', name: 'test2'}]
}
}
},
type: [null, undefined, 1]
}
const testObjWithArr2 = {
key: 'abc',
value: {
key: 'def',
value2: {
child: {
id: 'abc, def',
value: [{id: '1234', name: 'test2'}, {id: '123', name: 'test'}, {id: '1234', name: 'test2'}]
}
},
value: {
key: 'efg',
value: [{id: '123', name: 'test'}, {id: '1234', name: 'test2'}]
},
},
type: [1, null, undefined]
}
const sampleArr1 = ['1', '2']
const sampleArr2 = ['2', '1']
console.log(deepCompare(testData1, testData2)) // true
console.log(deepCompare(testObj1, testObj2)) // false
console.log(deepCompare(testObjWithArr1, testObjWithArr2)) // true
console.log(deepCompare(sampleArr1, sampleArr2)) // trues