Java教程

【源码阅读 | xe-utils源码 | 06】isEqual 深度比较两个值是否相等

本文主要是介绍【源码阅读 | xe-utils源码 | 06】isEqual 深度比较两个值是否相等,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1. 背景

  JavaScript 中包含基础的值类型引用类型其他类型
  关于值类型和引用类型的区别,若有不理解的可以看这篇文章补课 【JavaScript】深拷贝与浅拷贝 ,这里就不再赘述。
  当要判断 引用类型 时,以对象举例,则需要 遍历其所有的属性 来进行比较,只有属性对应的值完全相等,才能说两个对象相等,这在源码中如何实现呢?

2. 源码解析

2.1 xe-utils 源码

  先来看看 xe-utils 的源码,其中为了可扩展性,增加了 自定义判断函数
  如果你觉得过于复杂,可以直接阅读 2.2 中笔者以相同思路 简化过的代码,其中包含每个步骤的详细注释,可学习到的思路基本一致。

var isNumber = require("./isNumber");
var isArray = require("./isArray");
var isString = require("./isString");
var isRegExp = require("./isRegExp");
var isDate = require("./isDate");
var isBoolean = require("./isBoolean");
var isUndefined = require("./isUndefined");
var keys = require("./keys");
var every = require("./every");

function helperEqualCompare(val1, val2, compare, func, key, obj1, obj2) {
  if (val1 === val2) {
    return true;
  }
  if (
    val1 &&
    val2 &&
    !isNumber(val1) &&
    !isNumber(val2) &&
    !isString(val1) &&
    !isString(val2)
  ) {
    if (isRegExp(val1)) {
      return compare("" + val1, "" + val2, key, obj1, obj2);
    }
    if (isDate(val1) || isBoolean(val1)) {
      return compare(+val1, +val2, key, obj1, obj2);
    } else {
      var result, val1Keys, val2Keys;
      var isObj1Arr = isArray(val1);
      var isObj2Arr = isArray(val2);
      if (
        isObj1Arr || isObj2Arr
          ? isObj1Arr && isObj2Arr
          : val1.constructor === val2.constructor
      ) {
        val1Keys = keys(val1);
        val2Keys = keys(val2);
        if (func) {
          result = func(val1, val2, key);
        }
        if (val1Keys.length === val2Keys.length) {
          return isUndefined(result)
            ? every(val1Keys, function (key, index) {
                return (
                  key === val2Keys[index] &&
                  helperEqualCompare(
                    val1[key],
                    val2[val2Keys[index]],
                    compare,
                    func,
                    isObj1Arr || isObj2Arr ? index : key,
                    val1,
                    val2
                  )
                );
              })
            : !!result;
        }
        return false;
      }
    }
  }
  return compare(val1, val2, key, obj1, obj2);
}

module.exports = helperEqualCompare;

2.2 简化过后的代码

  代码中所用到的 isArrayisNumber 等判断基础类型的函数,都在前几期有提到过,还没看过的同学可以来复习一下
  【源码阅读 | xe-utils源码 | 01】判断基础类型
  【源码阅读 | xe-utils源码 | 02】判断Array类型
  【源码阅读 | xe-utils源码 | 03】isPlainObject | isTypeError
  【源码阅读 | xe-utils源码 | 04】isEmpty 判断是否空对象
  【源码阅读 | xe-utils源码 | 05】判断ES6中的新类型

2.2.1 有注释版本

const isArray = require('./isArray')
const isNumber = require('./isNumber')
const isRegExp = require('./isRegExp')
const isDate = require('./isDate')
const isBoolean = require('./isBoolean')
const isString = require('./isString')

function isEqual(o1, o2) {
  // 1.值类型比较:直接比较即可 [String | Number | Boolean | null | undefined]
  if (o1 === o2) return true

  // 2.正则:需将正则转为 string 再进行比较
  if (isRegExp(o1)) {
    return '' + o1 === '' + o2
  }

  // 3.日期和布尔
  if (isDate(o1) || isBoolean(o1)) {
    // + 号的作用:布尔能转为0和1;日期能转为时间戳;转换后直接通过数字型进行比较
    // 布尔:o1:true -> +o1:1
    // 日期:o1:Sun Jan 23 2022 10:34:09 GMT+0800 (中国标准时间) -> +o1:1642905249716
    return +o1 === +o2
  }

  // 4.引用类型比较
  if (
    o1 &&
    o2 &&
    !isNumber(o1) &&
    !isNumber(o2) &&
    !isString(o1) &&
    !isString(o2)
  ) {
    // 4.1 数组及其他类型
    const isO1Arr = isArray(o1)
    const isO2Arr = isArray(o2)

    // 若有一个为数组,则判断两个对象是否都为数组;若都不为数组,则判断对象实例是否属于同一个父类
    // 都为同一个类型后再进行深度比较,否则直接返回 false
    if (
      isO1Arr || isO2Arr
        ? isO1Arr && isO2Arr
        : o1.constructor === o2.constructor
    ) {
      let isE = true
      for (let key in o1) {
        // 若key非自身属性,则说明key属性原型链上,而非该实例上的属性,因此不相等
        if (!o2.hasOwnProperty(key)) {
          return false
        }

		// 重点!通过递归调用,不断深入进行比较
        isE = isEqual(o1[key], o2[key])

		// 一旦不相等,直接退出循环
        if (!isE) return isE
      }
      return isE
    } else {
      // 两个实例的类型不一样,直接返回false
      return false
    }
  }
  // 其他不符合的情况,如 [只传一个参数 | 两个不相等的String类型 ...]
  return false
}

2.2.2 无注释纯净版

const isArray = require('./isArray')
const isNumber = require('./isNumber')
const isRegExp = require('./isRegExp')
const isDate = require('./isDate')
const isBoolean = require('./isBoolean')
const isString = require('./isString')

function isEqual(o1, o2) {
  if (o1 === o2) return true

  if (isRegExp(o1)) {
    return '' + o1 === '' + o2
  }

  if (isDate(o1) || isBoolean(o1)) {
    return +o1 === +o2
  }

  if (
    o1 &&
    o2 &&
    !isNumber(o1) &&
    !isNumber(o2) &&
    !isString(o1) &&
    !isString(o2)
  ) {
    const isO1Arr = isArray(o1)
    const isO2Arr = isArray(o2)

    if (
      isO1Arr || isO2Arr
        ? isO1Arr && isO2Arr
        : o1.constructor === o2.constructor
    ) {
      let isE = true
      for (let key in o1) {
        if (!o2.hasOwnProperty(key)) {
          return false
        }
        isE = isEqual(o1[key], o2[key])
        if (!isE) return isE
      }
      return isE
    } else {
      return false
    }
  }
  return false
}

2.3 最终效果

console.log(isEqual({}, [])) // false
console.log(isEqual({ 0: 1 }, [1])) // false
console.log(isEqual(true, false)) // false
console.log(isEqual({ name: 'test1' }, { name: 'test1' })) // true
console.log(
  isEqual(
    { name: 'test1', list: [11, /\d/] },
    { name: 'test1', list: [11, /\d/] }
  )
) // true
console.log(
  isEqual(
    { name: 'test1', list: [11, 33, { a: /\D/ }] },
    { name: 'test1', list: [11, 33, { a: /\d/ }] }
  )
) // false
console.log(isEqual(new Date('2020-01-01'), new Date('2020-01-05'))) // false
这篇关于【源码阅读 | xe-utils源码 | 06】isEqual 深度比较两个值是否相等的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!