ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2022-01-23 12:34:12  阅读:162  来源: 互联网

标签:return && utils require 源码 key 06 o2 o1


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

标签:return,&&,utils,require,源码,key,06,o2,o1
来源: https://blog.csdn.net/weixin_42678675/article/details/122649428

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有