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 的作用是深度比较 objectother ,看这两个值是否相等。

一些变量

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
}

如果 objectBuffer 类型,而 other 又不是 Buffer 类型,则两者肯定不相等,直接返回 false

否则将 objIsArr 的标志位置为 trueobjIsObj 的标志位置为 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)
}

eqaulArraysequalByTag 的相关文章中,我们知道,stack 是拿来处理循环引用比较的,它是在这里初始化的。

上面处理 Buffer 的时候,如果 objectother 都为 Buffer 的时候, objeIsObj 的值会被设置为 false,也即 Buffer 的处理会落到这个分支。

接着判断,如果 objIsArr 也即为数组或 Buffer ,或者为 TypedArray 类型,都调用 equalArrays 方法来判断两者是否相等。

这也很容易理解,BufferTypedArray 其实就是字节数组,完全可以使用数组的比较方式。

至于其它类型,则使用 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)

最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:

作者:对角另一面

results matching ""

    No results matching ""