返回

_.isEqual源码逐行解析之巧用数组辨别NaN和+0

前端

前言

在JavaScript中,我们经常需要比较两个对象是否相等。然而,由于JavaScript的复杂性和一些特殊情况的存在,直接使用=====运算符进行比较并不总是可靠的。为了解决这个问题,lodash库提供了_.isEqual方法,它可以更全面和准确地比较两个对象是否相等。

源码解析

function isEqual(value, other) {
  // 第一步:如果值和其它值都为NaN,则返回true
  if (value !== value && other !== other) {
    return true;
  }
  // 第二步:如果值和其它值都为null,则返回true
  if (value === null && other === null) {
    return true;
  }
  // 第三步:如果值和其它值都为undefined,则返回true
  if (value === undefined && other === undefined) {
    return true;
  }
  // 第四步:如果值和其它值都是对象,则使用递归比较它们的属性
  if (isObjectLike(value) && isObjectLike(other)) {
    return objectIsEqual(value, other);
  }
  // 第五步:如果值和其它值都不是对象,则使用严格相等运算符进行比较
  return value === other;
}

第一步:处理NaN和+0的相等

_.isEqual方法的第一步是检查valueother是否都为NaN。如果两者都为NaN,则返回true。这是因为NaN是一个特殊的数值,它不等于任何其他值,包括它自己。因此,如果valueother都是NaN,那么它们一定是相等的。

if (value !== value && other !== other) {
  return true;
}

在JavaScript中,NaN+0是两个特殊的数值,它们在比较时具有特殊性。NaN代表“非数字”,它不等于任何其他值,包括它自己。+0代表“正零”,它等于0,但它们在某些情况下会被认为不相等。

为了正确处理NaN和+0的相等,_.isEqual方法采用了巧妙的设计。它使用!==运算符来比较valueother!==运算符是严格不相等运算符,它会比较两个值的类型和值是否都相同。如果valueother都是NaN,那么!==运算符会返回true,表明它们不相等。但是,由于NaN不等于任何其他值,所以value !== valueother !== other都会返回true。因此,如果valueother都是NaN,那么if (value !== value && other !== other)条件将为true,从而返回true,表明它们相等。

第二步:处理null和undefined的相等

_.isEqual方法的第二步和第三步分别检查valueother是否都为nullundefined。如果两者都为null或都为undefined,则返回true。这是因为nullundefined都是特殊的类型,它们不等于任何其他值,包括它们自己。

if (value === null && other === null) {
  return true;
}

if (value === undefined && other === undefined) {
  return true;
}

第四步:处理对象的相等

如果valueother都是对象,则_.isEqual方法使用递归比较它们的属性。递归比较是指在一个函数内部调用它自己。在_.isEqual方法中,递归比较是指比较两个对象的属性是否相等。

if (isObjectLike(value) && isObjectLike(other)) {
  return objectIsEqual(value, other);
}

isObjectLike函数用于判断一个值是否为类对象。类对象是指具有属性和方法的值,例如对象、数组和函数。

function isObjectLike(value) {
  return typeof value === 'object' && value !== null;
}

objectIsEqual函数用于比较两个对象的属性是否相等。它使用深度比较算法来比较对象的每个属性。深度比较算法是指不仅比较两个对象的属性是否相等,还会递归比较它们的子属性是否相等。

function objectIsEqual(object1, object2) {
  // 第一步:如果两个对象的属性数量不相同,则返回false
  if (Object.keys(object1).length !== Object.keys(object2).length) {
    return false;
  }

  // 第二步:遍历object1的每个属性
  for (const key in object1) {
    // 如果object2没有这个属性,则返回false
    if (!object2.hasOwnProperty(key)) {
      return false;
    }

    // 如果两个对象的属性值不相等,则返回false
    if (!_.isEqual(object1[key], object2[key])) {
      return false;
    }
  }

  // 第三步:如果所有属性都相等,则返回true
  return true;
}

第五步:处理基本类型的相等

如果valueother都不是对象,则_.isEqual方法使用严格相等运算符===进行比较。

return value === other;

严格相等运算符===会比较两个值的类型和值是否都相同。如果valueother都是基本类型,例如数字、字符串或布尔值,那么===运算符会正确地比较它们的相等性。

总结

通过逐行解析_.isEqual的源码,我们了解到它在处理NaN、+0、循环引用等特殊情况时采用了巧妙的设计。这些设计使_.isEqual方法能够更全面和准确地比较两个对象是否相等。

在实际开发中,我们可以使用_.isEqual方法来比较两个对象是否相等。例如,我们可以使用它来比较两个表单对象是否相等,或者比较两个数据对象是否相等。_.isEqual方法可以帮助我们更准确地判断两个对象是否相等,从而避免出现逻辑错误。