浅谈React 中的浅比较是如何工作的

React 中浅比较的概念无处不在,它在不同的流程中起着关键的作用,也可以在React组件的多个生命周期中找到。比如,React Hooks中的依赖数组,通过React.memo进行记忆。在React的官方文档中也多次提到“浅比较”这个概念,下面我们就来看看React中的浅比较是如何工作的!

想要理解浅比较的概念,最直接的方法就是研究React的源代码,下面就来看看React中的shallowEqual.js 文件:

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 作为类型检查系统。两个函数参数都是使用特殊的混合 Flow 类型定义,类似于 TypeScript 的 unknown,它表明函数可以是任何类型的值。

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

之后使用 React 内部对象的 is 函数将两个函数参数进行比较。导入的 is 函数只不过是JavaScript 的 Object.is 函数的polyfill 版本。 这个比较函数基本上等同于常见的 === 运算符,但有两个例外:

  • Object.is 认为 +0 和 -0 不相等,而 === 认为它们相等;
  • Object.is 认为 Number.NaN 和 NaN 相等,而 === 认为它们不相等。

基本上,第一个条件语句可以处理所有简单的情况:如果两个函数参数具有相同的值,对于原始类型,或引用相同的对象(数组和对象),那么通过浅比较认为它们相等的。

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;
  }

    // ...
}

现在就可以确保我们处理的是数组和对象了,接下来我们深入研究复杂数据结构的值,并在两个函数参数之间进行比较。在此之前,先来检查两个参数中值的数量是否相等,如果不相等,直接就可以确定两个值是不相等的。对于对象,得到的keys数组就是由实际的key组成的;对于数组,得到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;
  }

    // ...
}

最后一步,按照 key 来迭代两个函数参数的值,并逐个验证他们是否是相等的。为此,代码使用到了上一步中生成的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 源码学习了 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 中的浅比较是如何工作的的文章就介绍到这了,更多相关React 浅比较内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈React 的引入

    1. CDN 引入 和普通的 JS 库或框架一样,React 也可以从 CDN 引入. 在使用 CDN 引入之前需要了解两种类型的 JS 文件,一个是 cjs ,一个是 umd. cjs:全称为 CommonJS,是 Node.js 支持的模块规范 umd:全称为 Universal Module Definition 是统一模块定义,是包含 Node.js 使用的 cjs 的,兼容包含浏览器.Node.js 的模块规范. 新模块规范:最新的模块规范是使用 import 和 export 关键字

  • 详解React中Props的浅对比

    上一周去面试的时候,面试官我PureComponent里是如何对比props的,概念已经牢记脑中,脱口而出就是浅对比,接着面试官问我是如何浅对比的,结果我就没回答上来. 趁着周末,再来看看源码里是如何实现的. 类组件的Props对比 类组件是否需要更新需要实现shouldComponentUpdate方法,通常讲的是如果继承的是PureComponent则会有一个默认浅对比的实现. // ReactBaseClasses.js function ComponentDummy() {} Compo

  • 浅谈React原生APP更新

    App更新流程 1.在 App 打开时请求接口或文件, 获取远程版本/版本更新说明/地址等等重用信息 2.通过库或者原生方案, 获取 App 的当前版本 3.比较远程版本和当前版本的区别(可以使用库,也可以自己写一个比较方案) 4.通过获取到的链接进行操作(可以跳转到对应网站下载,类似蒲公英这种,可以是 apk 链接, 通过安卓原生方法下载, 也可以是 App Store 链接) 大致的流程图 详细说明: 1.这些远程信息,可以是接口, 这样可以有一个中台来控制, 当然也可以是一个文件, 让运维

  • 前端框架学习总结之Angular、React与Vue的比较详解

    近几年前端的技术发展很快,细分下来,主要可以分成四个方面: 1.开发语言技术,主要是ES6&7,coffeescript,typescript等: 2.开发框架,如Angular,React,Vue.js,Angular2等: 3.开发工具的丰富和前端工程化,像Grunt,Gulp,Webpack,npm,eslint,mocha这些技术: 4.前端开发范围的扩展,如服务端的nodejs,express,koa,meteor,GraphQL;移动端和跨平台的PhoneGap,ionic,Reac

  • 浅谈react 16.8版本新特性以及对react开发的影响

    目录 react16.8版本更新 useEffect react16.8版本更新解决了什么问题 组件复用更便捷 hooks和reactdiff算法 总结 Facebook团队对社区上的MVC框架都不太满意的情况下,开发了一套开源的前端框架react,于2013年发布第一个版本. react最开始倡导函数式编程,使用function以及内部方法React.creactClass创建组件,之后在ES6推出之后,使用类组件Class构建包含生命周期的组件. react 16.8版本更新 react16

  • 浅谈React 中的浅比较是如何工作的

    React 中浅比较的概念无处不在,它在不同的流程中起着关键的作用,也可以在React组件的多个生命周期中找到.比如,React Hooks中的依赖数组,通过React.memo进行记忆.在React的官方文档中也多次提到“浅比较”这个概念,下面我们就来看看React中的浅比较是如何工作的! 想要理解浅比较的概念,最直接的方法就是研究React的源代码,下面就来看看React中的shallowEqual.js 文件: import is from './objectIs'; import has

  • 浅谈React中组件逻辑复用的那些事儿

    基本每个开发者都需要考虑逻辑复用的问题,否则你的项目中将充斥着大量的重复代码.那么 React 是怎么复用组件逻辑的呢?本文将一一介绍 React 复用组件逻辑的几种方法,希望你读完之后能够有所收获.如果你对这些内容已经非常清楚,那么略过本文即可. 我已尽量对文中的代码和内容进行了校验,但是因为自身知识水平限制,难免有错误,欢迎在评论区指正. 1. Mixins Mixins 事实上是 React.createClass 的产物了.当然,如果你曾经在低版本的 react 中使用过 Mixins,

  • 浅谈React中组件间抽象

    关于今天要学习的组件间抽象其实我这小白看了几次还没弄明白,这次决定一探究竟.在组件构建中,通常有一类功能需要被不同的组件公用,此时就涉及抽象的概念,在React中我们主要了解mixin和高阶组件. mixin mixin的特性广泛存在于各个面向对象语言中,在ruby中,include关键词就是mixin,是将一个模块混入到另外一个模块中,或者是类中. 封装mixin方法 const mixin = function(obj, mixins) { const newObj = obj newObj

  • 浅谈React中的元素、组件、实例和节点

    React 深入系列,深入讲解了React中的重点概念.特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React. React 中的元素.组件.实例和节点,是React中关系密切的4个概念,也是很容易让React 初学者迷惑的4个概念.现在,老干部就来详细地介绍这4个概念,以及它们之间的联系和区别,满足喜欢咬文嚼字.刨根问底的同学(老干部就是其中一员)的好奇心. 元素 (Element) React 元素其实就是一个简单JavaScript对象,一个React 元素

  • 浅谈react.js中实现tab吸顶效果的问题

    在react项目开发中有一个需求是,页面滚动到tab所在位置时,tab要固定在顶部. 实现的思路其实很简单,就是判断当滚动距离scrollTop大于tab距离页面顶部距离offsetTop时,将tab的position变为fixed. 在react中,我在state中设置一个navTop属性,切换这个属性的值为true或者false,然后tab标签使用classnames()这个方法来利用navTop的值添加类名fixed. 一开始我是这样写的: import cs from 'classnam

  • 浅谈React 属性和状态的一些总结

    一.属性 1.第一种使用方法:键值对 <ClaaNameA name = "Tom" /> <ClaaNameA name = {Tom} /> <ClaaNameA name = {"Tom"} /> <ClaaNameA name = {[1,2,3]} />//数组 <ClaaNameA name = {FunctionNAme} /> //定义一个函数 2.第二种方法:三个点的展开对象形式 var

  • 浅谈react+es6+webpack的基础配置

    这是模块化开发.主流框架和最新版的ECMAScript语法规范的一个小demo 准备工作 安装 nodeJs 首先进入node官网,去下载最新版的nodeJs webpack 安装webpack npm install webpack -g 参数-g表示全局安装webpack,你在cmd命令中哪个文件夹下都可以使用webpack的命令,如果不加-g的话,是只可以在你安装webpack的目录下使用webpack这个命令 webpack 也有一个 web 服务器 npm install webpac

  • 浅谈react.js 之 批量添加与删除功能

    最近做的CMS需要用到批量添加图片的功能:在添加文件的容器盒子内,有两个内容,分别是:添加按钮与被添加的选择文件组件. 结构分析: 被添加的组件,我们称为:UploadQiNiuFiles(七牛文件上传组件),含一个删除当前组件的删除按钮 添加按钮的事件 被添加组件存放的容器 做这个效果只需要明白三个方法的用途就OK: 直接绑定要删除组件的  deleteType(),它是调用删除index数量的方法  removeContent() //删除{qiniu}与{deleteQiNiu}内容,是把

  • 浅谈React Native打包apk的坑

    RN的打包,大家可以根据官网一步一步来,但这里有几个地方注意,一下简单介绍: 生成一个签名密钥 在项目的目录下打开cmd命令窗口输入一下命令运行: keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000 这条命令会要求你输入密钥库(keystore)和对应密钥的密码,然后设置一些发行相关的信息.最后它会生成一个叫做my-re

  • 浅谈react受控组件与非受控组件(小结)

    我们都知道,有许多的web组件可以被用户的交互发生改变,比如:<input>,<select>,或者是我现在正在使用的富文本编辑器.这些组件在日常的开发中很不显眼,我们可以很轻易的通过输入一些内容或者设置元素的value属性来改变组件的值.但是,因为React是单向数据流绑定的,这些组件可能会变得失控: 1.一个维护它自己state里的value值的<Input>组件无法从外部被修改: 2.一个通过props来设置value值的<Input>组件只能通过外部

随机推荐