lodash源码分析之baseIsEqualDeep
本文为读 lodash 源码的第二百一十八篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash
gitbook也会同步仓库的更新,gitbook地址:pocket-lodash
依赖
import Stack from './Stack.js'
import equalArrays from './equalArrays.js'
import equalByTag from './equalByTag.js'
import equalObjects from './equalObjects.js'
import getTag from './getTag.js'
import isBuffer from '../isBuffer.js'
import isTypedArray from '../isTypedArray.js'
《lodash源码分析之Stack》 《lodash源码分析之equalArrays》 《lodash源码分析之equalByTag》 《lodash源码分析之equalObjects》 《lodash源码分析之getTag》 《lodash源码分析之isBuffer》 《lodash源码分析之isTypedArray》
源码分析
baseIsEqualDeep
的作用是深度比较 object
和 other
,看这两个值是否相等。
一些变量
const COMPARE_PARTIAL_FLAG = 1
const argsTag = '[object Arguments]'
const arrayTag = '[object Array]'
const objectTag = '[object Object]'
const hasOwnProperty = Object.prototype.hasOwnProperty
COMPARE_PARTIAL_FLAG
用来保存是否局部比较的标识。
argsTag
用来保存 arguments
对象通过 Object.prototype.toString.call
后得到的结果,这里统称为 tag
。
arrayTag
用来保存 Array
对象的 tag
。
objectTag
用来保存 Object
对象的 tag
。
hasOwnProperty
用来保存 Object.prototype.hasOwnProperty
,主要是方便调用来优化性能。
处理arrayTag
let objIsArr = Array.isArray(object)
const othIsArr = Array.isArray(other)
let objTag = objIsArr ? arrayTag : getTag(object)
let othTag = othIsArr ? arrayTag : getTag(other)
后面 Array
是分开处理的,这里使用 Array.isArray
来判断是否为数组,没有直接使用 getTag
方法,是因为 Array.isArray
性能会更加好。
处理argsTag
objTag = objTag == argsTag ? objectTag : objTag
othTag = othTag == argsTag ? objectTag : othTag
可以看到,argsTag
会统一成 objectTag
,也即后续 arguments
对象会跟对象一样,使用同样的比较方式。
处理Tag相同的情况
判断Tag是否相同
let objIsObj = objTag == objectTag
const othIsObj = othTag == objectTag
const isSameTag = objTag == othTag
使用 objTag == othTag
即可判断 Tag
是否相同。
处理Buffer
if (isSameTag && isBuffer(object)) {
if (!isBuffer(other)) {
return false
}
objIsArr = true
objIsObj = false
}
如果 object
是 Buffer
类型,而 other
又不是 Buffer
类型,则两者肯定不相等,直接返回 false
。
否则将 objIsArr
的标志位置为 true
,objIsObj
的标志位置为 false
。也即将 object
标记为数组。
处理非对象类型
if (isSameTag && !objIsObj) {
stack || (stack = new Stack)
return (objIsArr || isTypedArray(object))
? equalArrays(object, other, bitmask, customizer, equalFunc, stack)
: equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack)
}
在 eqaulArrays
和 equalByTag
的相关文章中,我们知道,stack
是拿来处理循环引用比较的,它是在这里初始化的。
上面处理 Buffer
的时候,如果 object
和 other
都为 Buffer
的时候, objeIsObj
的值会被设置为 false
,也即 Buffer
的处理会落到这个分支。
接着判断,如果 objIsArr
也即为数组或 Buffer
,或者为 TypedArray
类型,都调用 equalArrays
方法来判断两者是否相等。
这也很容易理解,Buffer
和 TypedArray
其实就是字节数组,完全可以使用数组的比较方式。
至于其它类型,则使用 equalByTag
来比较,equalByTag
方法,会根据 Tag
的不同,选择不同的比较方式。
处理lodash包装对象
if (!(bitmask & COMPARE_PARTIAL_FLAG)) {
const objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__')
const othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__')
if (objIsWrapped || othIsWrapped) {
const objUnwrapped = objIsWrapped ? object.value() : object
const othUnwrapped = othIsWrapped ? other.value() : other
stack || (stack = new Stack)
return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack)
}
}
包装对象是 lodash
所定义的一种数据格式,会有 __wrapped__
属性,这种对象会延迟计算,只有在显示调用 value
方法时才会计算值。
因此遇到这种对象,会调用 value
方法得到真正的值,再使用 equalFunc
进行比较。
处理对象
if (!isSameTag) {
return false
}
stack || (stack = new Stack)
return equalObjects(object, other, bitmask, customizer, equalFunc, stack)
如果两个值的 Tag
不一样,因为类型都不一样,两个值肯定不相等,直接返回 false
。
最终最终,来到了对象的比较,调用 equalObjects
函数进行对象比较即可。
License
署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)
最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:
作者:对角另一面