深入解析 React 浅比较机制与源码实现
本文深入解析 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 浅比较的工作原理,更多相关内容可搜索本站历史文章或继续浏览后续文章,感谢支持。
sigma.js 快速上手:在原生 JS 与 React 中绘制第一个网络图
2026-11-03 23:23:32
UmiJS 约定式路由配置与权限拦截实战
2026-11-03 22:12:08
JS前端文件读取FileReader操作方法总结
2026-10-31 15:12:21
JS面试必备:如何实现一个精确的倒计时
2026-10-31 12:14:23
从 Pages Router 迁移到 Next.js App Router 的注意事项
2026-10-30 19:14:52







