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)

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

作者:对角另一面

results matching ""

    No results matching ""