「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」
前言
大家好,我是coder__chen。在一个平平无奇的下午我准备划完水下班的时候,桌面右下角的微信消息图标突然闪动起来,点开一看原来是朋友发的消息:能不能写一个深对比函数
方法实现
首先依据题意已知要进行比较的数组是一个不定层级,且数组元素类型可以是任意类型,任意深度。如果元素是基本数据类型如Number、String、Boolean、Synbol、Undefined,Null那直接使用全等运算符进行比较即可。如果元素类型是Function这直接调用函数的toString方法对函数体字符串形式进行比较即可。但是如果元素类型时Array、Object那就需要进行再次遍历每一个改元素的子元素进行对比,此时聪明的你肯定想到了通过递归函数的来进行比较不就好了。大体思路已经构思好了那就开干吧!
定义类型判断函数
工欲善其事必先利其器,根据刚才的思路先把几个一会需要用到的类型判断函数定义好。
// 判断数据类型是否为对象
const isObject = obj => Object.prototype.toString.call(obj) === '[object Object]'
// 判断数据类型是否为数组
const isArray = arr => Array.isArray(arr)
// 判断数据类型是否为函数
const isFunction = func => typeof func === 'function'
// 判断数据类型是否为日期对象
const isDate = date => Object.prototype.toString.call(date) === '[object Date]'
编写数组深对比函数deepCompareArray
/**
*数组深度比较函数
* @param {Array} arr1 待比较数组1
* @param {Array} arr2 待比较数组2
* @returns Boolean 比较结果
*/
const deepCompareArray = (arr1, arr2) => {
// 判断传入的两个参数是否为都为数组,如果有一个不是直接返回false
if (!isArray(arr1) || !isArray(arr2)) return false
// 判断传入的两个数组的栈地址值是否相同,相同直接返回true
if (arr1 === arr2) return true
// 以上过滤条件都不满足开始循环递归比较
return compareData(arr1, arr2)
}
编写递归逻辑
/**
* 比较对象或数组是否相同
* @param {Array|Object} data1 待比较的对象或数组1
* @param {Array|Object} data2 待比较的对象或数组2
* @returns Boolean 比较结果
*/
const compareData = (data1, data2) => {
// 判断传入数据的类型是否为数组,由于前面逻辑已经确定二者数据类型一致,所以只需知道一个就行
const isArrayFlag = isArray(data1)
// 获取data1的元素个数
const dataLength1 = isArrayFlag ? data1.length : Object.keys(data1).length
// 获取data2的元素个数
const dataLength2 = isArrayFlag ? data2.length : Object.keys(data2).length
// 判断传入的两个数组的长度是否相同,不同直接返回false
if (dataLength1 !== dataLength2) return false
// 判断传入的两个数组是否都空数组或空对象,是直接返回true
if (dataLength1 === 0 && dataLength2) return true
/**
* 由于for...in可以遍历数组和对象类型的数据,但会把原型链上的数据也一起遍历出来。
* 所以将数据进行一次浅拷贝便能去除掉原型链的影响
*/
const copyData1 = isArrayFlag ? [...data1] : { ...data1 }
const copyData2 = isArrayFlag ? [...data2] : { ...data2 }
// 判断标识 默认为true
let flag = true
// 通过for...in循环数组或对象 进行对应索引项比较
for (let i in copyData1) {
// 对应索引项值不等进入可能值判断
if (copyData1[i] !== copyData2[i]) {
switch (true) {
// 如果二者都是数组或都是普通对象则递归调用compareData再次循环逐个判断
case (isObject(copyData1[i]) && isObject(copyData2[i])) ||
(isArray(copyData1[i]) && isArray(copyData2[i])):
flag = compareData(copyData1[i], copyData2[i])
break
// 如果二者都是函数则判断函数体内容是否一致
case isFunction(copyData1[i]) && isFunction(copyData2[i]):
flag = copyData1[i].toString() === copyData2[i].toString()
break
// 如果二者都是日期对象则判断时间戳是否一致
case isDate(copyData1[i]) && isDate(copyData2[i]):
flag = copyData1[i].valueOf() === copyData2[i].valueOf()
break
// 不满足以上条件再判断两个比较数是不是都为NaN
default:
flag = Number.isNaN(copyData1[i]) && Number.isNaN(copyData2[i])
}
}
// 如果比较标识为false则证明不相同,立即停止循环
if (!flag) break
}
// 返回比较结果
return flag
}
写完以上逻辑数组的深比对大体就完成了,接下来就可以来测试以下效果了。
// 先定义两个有一定复杂度的数据:
const date1 = new Date('2022-01-20 13:23:42')
const date2 = new Date('2022-01-20 13:23:42')
const list1 = [
1,
2,
3,
{
a: 2,
b: 3,
d: { a: { e: { g: { b: 2 } } }, y: [{ a: 2 }, { w: 33 }] },
c: () => 1
},
4,
5,
6,
NaN,
date1
]
const list2 = [
1,
2,
3,
{
a: 2,
b: 3,
d: { a: { e: { g: { b: 2 } } }, y: [{ a: 2 }, { w: 33 }] },
c: () => 1
},
4,
5,
6,
NaN,
date2
]
// 使用深比对函数
console.log(deepCompareArray(list1, list2))
运行后输出结果为true
小结
至此数组深比对工具函数逻辑就编写完了,文中主要运用的是递归的思路实现,利用for...in方法即可循环对象又可以循环数组的特性,对于对象和数组类型的元素就可以放在一起统一处理。上述的写法稍微改造改造还能进行对象深比对,感兴趣的同学可以动手改造下试试。
如果觉得本文有帮助 记得点赞三连哦 十分感谢!