深入解析 React 浅比较机制与源码实现

作者:晴川时间:2026-10-26 01:15:15浏览:169

本文深入解析 React 源码中的 shallowEqual 函数,揭示其基于 Object.is 的浅比较机制。通过拆解源码逻辑,详细阐述了对原始类型、引用类型及复杂结构的处理流程,并总结了空对象与数组等价性等关键特性,帮助开发者理解 React 性能优化的底层原理。

React 浅比较机制概述

浅比较机制在 React 生态系统中应用广泛,贯穿多个核心流程与组件生命周期。例如,React Hooks 的依赖数组检查以及使用 React.memo 进行的组件记忆化,均依赖于这一机制。官方文档多次提及“浅比较”,本文将深入剖析其具体工作原理。

理解浅比较最直接的方式是研读 React 源码,重点分析 packages/shared/shallowEqual.js 文件。

shallowEqual 源码结构

import is from './objectIs';
import hasOwnProperty from './hasOwnProperty';

/**
 * Performs equality by iterating through keys on an object and returning false
 * when any key has values which are not strictly equal between the arguments.
 * Returns true when the values of all keys are strictly equal.
 */
function shallowEqual(objA: mixed, objB: mixed): boolean {
  if (is(objA, objB)) {
    return true;
  }

  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {
    return false;
  }

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }
  
  // Test for A's keys different from B.
  for (let i = 0; i < keysA.length; i++) {
    const currentKey = keysA[i];
    if (
      !hasOwnProperty.call(objB, currentKey) ||
      !is(objA[currentKey], objB[currentKey])
    ) {
      return false;
    }
  }

  return true;
}

函数定义与类型检查

该函数执行步骤较多,现将其拆解逐步分析。首先查看函数定义,它接收两个待比较对象作为参数。代码采用 Flow 类型检查系统,两个参数被定义为特殊的混合类型(mixed),类似于 TypeScript 中的 unknown,表示参数可以是任意类型的值。

function shallowEqual(objA: mixed, objB: mixed): boolean {
    // ...
}

初步比较与 Object.is 特性

随后,函数调用 React 内部的 is 函数对两个参数进行初步比较。该 is 函数是 JavaScript 原生 Object.is 的 polyfill 实现。其逻辑大致等同于严格相等运算符 ===,但存在两个关键区别:

  • Object.is 判定 +0 与 -0 不相等,而 === 判定它们相等;
  • Object.is 判定 Number.NaN 与 NaN 相等,而 === 判定它们不相等。

首个条件语句处理了所有简单情况:若两个参数值相同(对于原始类型)或引用同一对象(对于数组和对象),则浅比较直接返回 true。

import is from './objectIs';

function shallowEqual(objA: mixed, objB: mixed): boolean {
  if (is(objA, objB)) {
    return true;
  }
    // ...
}

复杂结构判定逻辑

处理完值相等或引用相同的简单情况后,进入复杂结构(数组和对象)的处理逻辑。为确保处理的是复杂结构,代码检查任一参数是否为 object 类型或等于 null(用于过滤空值,因为 typeof null 返回 'object')。若任一条件成立,说明两个参数不相等(否则会被前一步过滤),因此直接返回 false。

function shallowEqual(objA: mixed, objB: mixed): boolean {
    // ...

  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {
    return false;
  }

    // ...
}

键值数量与类型检查

确认处理的是数组或对象后,深入比较其内部值。首先检查两个参数中值的数量是否相等,若不等则直接返回 false。对于对象,keys 数组由实际键名组成;对于数组,keys 数组由字符串类型的索引组成。

function shallowEqual(objA: mixed, objB: mixed): boolean {
    // ...

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

    // ...
}

属性迭代与最终验证

最后一步,依据生成的 keys 数组迭代两个参数的值,并逐个验证相等性。代码利用 hasOwnProperty 检查 key 是否真实存在于参数中,并使用 Object.is 函数进行比较。

import hasOwnProperty from './hasOwnProperty';

function shallowEqual(objA: mixed, objB: mixed): boolean {
    // ...

  // Test for A's keys different from B.
  for (let i = 0; i < keysA.length; i++) {
    const currentKey = keysA[i];
    if (
      !hasOwnProperty.call(objB, currentKey) ||
      !is(objA[currentKey], objB[currentKey])
    ) {
      return false;
    }
  }

  return true;
}

若任意 key 对应的值不相等,则判定两个对象不相等,直接返回 false 并结束循环。若所有值均相等,则返回 true。

浅比较核心特性总结

通过 React 源码学习,我们总结了浅比较中的几个关键特性:

  • 浅比较基于 Object.is 函数,而非严格相等运算符 ===;
  • 通过浅比较,空对象 {} 和空数组 [] 被视为等价;
  • 通过浅比较,以数组索引为 key、数组值为 value 的对象(如 { 0: 2, 1: 3 })与数组([2, 3])等价;
  • 由于 Object.is 判定 +0 与 -0、Number.NaN 与 NaN 不相等,这一规则在复杂结构比较中同样适用;
  • 虽然 {} 和 [] 浅比较相等,但嵌套在对象中的对象不相等,例如 { someKey: {} } 和 { someKey: [] } 不相等。

参考源码:https://github.com/facebook/react/blob/main/packages/shared/shallowEqual.js

本文介绍了 React 浅比较的工作原理,更多相关内容可搜索本站历史文章或继续浏览后续文章,感谢支持。

作者最新文章
相关文章
更多