【React】PureComponent 和 Component 的区别

前言

在 React 中,PureComponent 和 Component 都是用于创建组件的基类,但它们有一个主要的区别:PureComponent 会给类组件默认加一个shouldComponentUpdate周期函数。在此周期函数中,它对props 和 state (新老的属性/状态)会做一个浅比较只比较第一层状态主要指的是内存地址是否发生变化。如果经过浅比较,发现属性和状态并没有变化,则返回fasle,也就是不继续更新组件,有变化才会去更新。

一、shouldComponentUpdate 使用

 shouldComponentUpdate(nextProps, nextState) {let { props, state } = this;// props/state:修改之前的属性状态// nextProps/nextState:将要修改的属性状态return !shallowEqual(props, nextProps) || !shallowEqual(state, nextState);// return true 说明需要更新,false 说明不需要更新} 

二、浅比较

在这里插入图片描述
上图是一个浅比较的逻辑。

这里我们实现一个浅比较的逻辑,主要是比较props
state。首先需要一个判断是否为对象的方法。

  • 判断传过来的参数不为null,且参数类型为object或者function
const isObject = function isObject(obj){return obj !== null && /^(object|function)$/.test(typeof obj);
}

比较两个对象的属性和状态

  • 判断传过的对象是否为对象
  • 判断对象是否相等
  • 接下来就开始判断两个对象的key、value
    • 判断两个对象的长度是否相等(通过Reflect.ownkeys(obj)获取key)
    • 循环其中一个对象的key集合,判断该对象的key是否在另一个对象里,如果在,则判断两个对象相同keyvalue 值是否相等

// 对象浅比较的方法
const shallowEqual = function shallowEqual(objA, objB) {if (!isObject(objA) || !isObject(objB)) return false;if (objA === objB) return true;// 先比较成员的数量let keysA = Reflect.ownKeys(objA),keysB = Reflect.ownKeys(objB);if (keysA.length !== keysB.length) return false;// 数量一致,再逐一比较内部的成员「只比较第一级:浅比较」for (let i = 0; i < keysA.length; i++) {let key = keysA[i];// 如果一个对象中有这个成员,一个对象中没有;或者,都有这个成员,但是成员值不一样;都应该被判定为不相同!!if (!objB.hasOwnProperty(key) || !Object.is(objA[key], objB[key])) {return false;}}// 以上都处理完,发现没有不相同的成员,则认为两个对象是相等的return true;
};

三、浅比较组件内使用

在使用React.PureComponent时候,组件内会默认加一个shouldComponentUpdate周期函数实现浅比较的功能,但是使用React.Component 要想使用shouldComponentUpdate周期函数的功能,就需要手动添加shouldComponentUpdate,且手动判断属性和状态是否改变。

class Demo extends React.Component {state = {arr: [10, 20, 30] //0x001};render() {let { arr } = this.state; //arr->0x001return <div>{arr.map((item, index) => {return <span key={index} style={{display: 'inline-block',width: 100,height: 100,background: 'pink',marginRight: 10}}>{item}</span>;})}<br /><button onClick={() => {arr.push(40); //给0x001堆中新增一个40// this.setState({ arr }); //最新修改的转态地址,还是0x001「状态地址没有改」 // console.log(this.state.arr); //[10,20,30,40]/* // 无法更新的*/// this.forceUpdate(); //跳过默认加的shouldComponentUpdate,直接更新this.setState({arr: [...arr] //我们是让arr状态值改为一个新的数组「堆地址」})}}>新增SPAN</button></div >;}shouldComponentUpdate(nextProps, nextState) {let { props, state } = this;return !shallowEqual(props, nextProps) || !shallowEqual(state, nextState);}
}

修改arr的值,并使用setState修改arr的状态,发现页面的状态并没有变化,这是因为,状态变了,但是arr还是之前的状态地址。

  <button onClick={() => {arr.push(40); //给0x001堆中新增一个40this.setState({ arr }); //最新修改的转态地址,还是0x001「状态地址没有改」 }}>新增SPAN</button>

此时想要页面状态改变则有两种方式:

(1)方式一:使用this.forceUpdate(),直接跳过shouldComponentUpdate周期,所以页面一定会更改状态并渲染

<button onClick={() => {arr.push(40); //给0x001堆中新增一个40this.setState({ arr }); //最新修改的转态地址,还是0x001「状态地址没有改」 this.forceUpdate(); //跳过默认加的shouldComponentUpdate,直接更新}}>新增SPAN</button>

(2)方式二:修改arr的状态地址

使用this.setState({ arr: [...arr] }),此时arr的引用地址就不是之前的引用地址,是一个全新的数组,则objA === objB就为false

       <button onClick={() => {arr.push(40); //给0x001堆中新增一个40this.setState({ arr }); //最新修改的转态地址,还是0x001「状态地址没有改」 this.setState({arr: [...arr] //我们是让arr状态值改为一个新的数组「堆地址」})}}>新增SPAN</button>

四、拓展

1、为什么使用Reflect.ownKeys(obj)获取对象的属性,而不是用Object.keys()?

特性Object.keys()Reflect.ownKeys()
返回的键类型仅普通属性的键(字符串)普通属性键 + 符号属性键(字符串和符号)
返回的属性类型仅可枚举属性包含所有自有属性(可枚举与不可枚举属性)
是否支持符号属性
适用场景处理普通属性,只关心普通属性或只关心可枚举属性全面访问对象所有自有属性(包括符号属性和不可枚举属性),用于更底层的操作或元编程场景

Reflect.ownKeys(obj) 是一种比 Object.keys(obj) 更通用的方式来获取对象的所有键名,包括普通属性和Symbol 属性。

Object.keys(obj) 只能返回对象的普通属性(即非符号属性)。它会遍历对象的自有可枚举属性,并返回一个数组。不可枚举属性(比如通过 Object.defineProperty 设置为不可枚举的属性)和符号属性都不会被返回。

示例:

const obj = {normalKey: 'value',[Symbol('symbolKey')]: 'symbolValue'
};console.log(Object.keys(obj));  // 输出: ["normalKey"]

在上述代码中,Object.keys(obj) 只返回了 normalKey,没有返回符号属性。

Reflect.ownKeys(obj) 是一个更通用的方式,它会返回对象的所有自有键名,无论是普通属性还是符号属性。返回的结果是一个包含字符串和符号的数组。

示例:

const obj = {normalKey: 'value',[Symbol('symbolKey')]: 'symbolValue'
};console.log(Reflect.ownKeys(obj));  // 输出: ["normalKey", Symbol(symbolKey)]

在这个示例中,Reflect.ownKeys(obj) 返回了 normalKey 和符号属性 Symbol(symbolKey),这使得它比 Object.keys(obj) 更为强大和灵活。

示例:结合 Reflect.ownKeys() 使用:

const obj = {normalKey: 'value'
};Object.defineProperty(obj, 'hiddenKey', {value: 'hiddenValue',enumerable: false
});const sym = Symbol('symbolKey');
obj[sym] = 'symbolValue';console.log(Object.keys(obj));  // 输出: ["normalKey"]
console.log(Reflect.ownKeys(obj));  // 输出: ["normalKey", "hiddenKey", Symbol(symbolKey)]

在这个示例中,Object.keys(obj) 只返回了 normalKey,而 Reflect.ownKeys(obj) 返回了所有自有键,包括不可枚举的 hiddenKey 和符号属性 Symbol(symbolKey)

2、为什么使用hasOwnProperty()获取对象的属性,而不是用in?

objB.hasOwnProperty(key) 是 JavaScript 中用于判断对象 objB 是否具有某个自有属性 key 的方法。

hasOwnProperty()Object 的一个原型方法,属于所有对象实例。它用于检查给定的属性是否是对象自身的直接属性,而不是从原型链继承来的属性。

语法

obj.hasOwnProperty(key)

示例 1:检查自有属性

const objB = {name: 'Alice',age: 25
};console.log(objB.hasOwnProperty('name'));  // 输出: true
console.log(objB.hasOwnProperty('age'));   // 输出: true
console.log(objB.hasOwnProperty('gender')); // 输出: false

在这个例子中,objBnameage 这两个自有属性,因此 hasOwnProperty('name')hasOwnProperty('age') 返回 true。而 gender 并没有在 objB 上定义,所以返回 false

示例 2:检查继承的属性

const person = {name: 'Alice'
};const employee = Object.create(person);
employee.age = 25;console.log(employee.hasOwnProperty('age'));   // 输出: true
console.log(employee.hasOwnProperty('name'));  // 输出: false

employee 对象通过 Object.create(person) 创建,继承了 person 的属性。因此,employee.hasOwnProperty('name') 返回 false,因为 name 是从原型链继承来的,而 ageemployee 自己定义的属性,所以返回 true

in 操作符的区别

in 操作符会检查对象及其原型链上是否存在指定的属性,而 hasOwnProperty() 只检查对象自身的属性,不会查找原型链上的属性。

示例 3:区别

const person = {name: 'Alice'
};const employee = Object.create(person);
employee.age = 25;console.log('age' in employee);           // 输出: true
console.log('name' in employee);          // 输出: true
console.log(employee.hasOwnProperty('age')); // 输出: true
console.log(employee.hasOwnProperty('name')); // 输出: false
  • 'age' in employee 会返回 true,因为 employee 对象自身有 age 属性。
  • 'name' in employee 会返回 true,因为 name 是从原型 person 继承的。
  • employee.hasOwnProperty('age') 返回 true,因为 ageemployee 自有的属性。
  • employee.hasOwnProperty('name') 返回 false,因为 name 是从原型链继承来的。

使用 hasOwnProperty 时的注意点

  • 性能hasOwnProperty() 只检查自有属性,性能上优于检查属性是否存在于对象及其原型链中(例如,使用 in 操作符或 for...in 循环)。
  • 避免属性名冲突:如果对象本身有一个名为 hasOwnProperty 的属性,可能会导致调用方法时发生冲突。因此,使用 hasOwnProperty 时可以通过 Object.prototype.hasOwnProperty.call() 来确保方法的正确调用:

示例 4:避免属性冲突

const objB = {hasOwnProperty: 'some value',name: 'Alice'
};console.log(objB.hasOwnProperty('name'));  // 输出: TypeError: objB.hasOwnProperty is not a function// 使用 Object.prototype.hasOwnProperty 来避免冲突
console.log(Object.prototype.hasOwnProperty.call(objB, 'name'));  // 输出: true

3、为什么使用Object.is(objA,objB)

Object.is(objA[key], objB[key]) 是 JavaScript 中用于比较两个值是否严格相等的方法,类似于 ===(严格相等操作符),但在某些特殊情况下行为有所不同。Object.is 方法比较的是两个值是否相同,返回 true 表示相等,返回 false 表示不相等。

Object.is=== 在大多数情况下行为相同,但是它们有两个显著的不同点:

  1. NaN:在 === 中,NaN 不等于 NaN,但是在 Object.is 中,NaN 等于 NaN
  2. +0-0:在 === 中,+0-0 被认为是相等的,但是在 Object.is 中,+0-0 被认为是不同的。

示例 1:NaN 比较

console.log(Object.is(NaN, NaN));  // 输出: true
console.log(NaN === NaN);          // 输出: false

在这个例子中,Object.is(NaN, NaN) 返回 true,而 NaN === NaN 返回 false。这是 Object.is=== 的区别之一。

示例 2:+0-0 比较

console.log(Object.is(+0, -0));  // 输出: false
console.log(+0 === -0);          // 输出: true

在这个例子中,Object.is(+0, -0) 返回 false,而 +0 === -0 返回 true。这是由于 Object.is 会区分 +0-0

使用场景

Object.is 适用于需要精确比较两个值是否完全相同,特别是在以下情况下:

  • 比较 NaN 是否相等。
  • 比较 +0-0 是否相等。
  • 比较对象或数组的引用是否相同。

示例 3:在比较对象属性时使用 Object.is

const objA = { key: 42 };
const objB = { key: 42 };console.log(Object.is(objA.key, objB.key));  // 输出: true,因为值相等

示例 4:比较引用类型(对象、数组)

const arrA = [1, 2, 3];
const arrB = [1, 2, 3];console.log(Object.is(arrA, arrB));  // 输出: false,因为它们是不同的引用const arrC = arrA;
console.log(Object.is(arrA, arrC));  // 输出: true,因为它们引用的是同一个数组

四、使用===判断对象是否相等

if (objA === objB) return true; 的含义是判断 objAobjB 是否是相等的,并在它们相等时返回 true。这里使用的是严格相等运算符 ===

  1. === 运算符:

    • 严格相等=== 是严格相等运算符,它会同时比较值和类型。如果两个操作数的类型不同,返回 false
    • 对于引用类型(例如对象、数组、函数等),=== 比较的是内存地址,即两个变量是否引用同一个对象。
  2. 对象的比较:

    • 如果 objAobjB 都是对象类型(包括数组、函数等),=== 会检查它们是否指向同一个对象。
    • 如果它们指向同一个内存地址(即是同一个对象),则返回 true
    • 如果它们是不同的对象,虽然对象的内容可能相同,但它们的内存地址不同,因此返回 false

示例:

let objA = { name: 'Alice' };
let objB = { name: 'Alice' };
let objC = objA;console.log(objA === objB); // false, 因为它们是不同的对象,虽然内容相同
console.log(objA === objC); // true, 因为 objC 是 objA 的引用,指向同一个对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/10616.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二级C语言:二维数组每行最大值与首元素交换、删除结构体的重复项、取出单词首字母

目录 一、程序填空 --- 二维数组每行最大值与首元素交换 题目 分析 知识点 --- 交换语句 二、程序修改 --- 删除结构体的重复项 题目 分析 三、程序设计 --- 取出单词首字母 题目 分析 前言 本章讲解&#xff1a;二维数组每行最大值与首元素交换、删除结构体的重复项…

CUDA学习-内存访问

一 访存合并 1.1 说明 本部分内容主要参考: 搞懂 CUDA Shared Memory 上的 bank conflicts 和向量化指令(LDS.128 / float4)的访存特点 - 知乎 1.2 share memory结构 图1.1 share memory结构 放在 shared memory 中的数据是以 4 bytes(即 32 bits)作为 1 个 word,依…

【python】python基于机器学习与数据分析的手机特性关联与分类预测(源码+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、测试技术。 python基于机器学习与数据分析的手机特性关联与分类…

【leetcode详解】T3175(一点反思)

解题心得 要写出一个好的程序&#xff0c;有效解决问题&#xff0c;思路上就不能“太乖” —— 不能被题目的叙述过程所束缚&#xff0c;而是力求细思问题&#xff0c;抽象化问题&#xff0c;并找到背后的逻辑&#xff1b;最后抓住核心对象&#xff0c;去除多余项&#xff0c;…

图论——最小生成树

最小生成树 给定一个无向图&#xff0c;在图中选择若干条边把图的所有节点连起来。要求边长之和最小。在图论中&#xff0c;叫做求最小生成树。 prim算法 prim 算法采用的是一种贪心的策略。 每次将离连通部分的最近的点和点对应的边加入的连通部分&#xff0c;连通部分逐渐扩大…

jvisualvm工具使用

jvisualvm 是JDK自带的具有图形界面操作功能的JVM性能监控和诊断工具&#xff0c;它不仅能分析和诊断堆转储文件&#xff0c;在线实时监控本地JVM进程&#xff0c;还能监控远程服务器上的JVM进程。 1 分析服务器下载dump文件 1&#xff09;在我们在安装JDK的bin目录双击jvisa…

C++ list

list需知&#xff1a; list不会出现insert迭代器失效问题 链表插入不会影响原有数据相对位置&#xff0c;且不用扩容 但是erase会导致相对数据位置移动&#xff0c;所有其erase会导致迭代器失效 list排序效率很低 不建议使用 小规模数据量可以使用&#xff0c;比较方便 此外…

DeepSeek-R1 论文解读 —— 强化学习大语言模型新时代来临?

近年来&#xff0c;人工智能&#xff08;AI&#xff09;领域发展迅猛&#xff0c;大语言模型&#xff08;LLMs&#xff09;为通用人工智能&#xff08;AGI&#xff09;的发展开辟了道路。OpenAI 的 o1 模型表现非凡&#xff0c;它引入的创新性推理时缩放技术显著提升了推理能力…

【基于SprintBoot+Mybatis+Mysql】电脑商城项目之用户注册

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录 &#x1f3af;项目基本介绍 &#x1f6a6;项…

蓝桥杯思维训练营(一)

文章目录 题目总览题目详解翻之一起做很甜的梦 蓝桥杯的前几题用到的算法较少&#xff0c;大部分考察的都是思维能力&#xff0c;方法比较巧妙&#xff0c;所以我们要积累对应的题目&#xff0c;多训练 题目总览 翻之 一起做很甜的梦 题目详解 翻之 思维分析&#xff1a;一开…

【AI】DeepSeek 概念/影响/使用/部署

在大年三十那天&#xff0c;不知道你是否留意到&#xff0c;“deepseek”这个词出现在了各大热搜榜单上。这引起了我的关注&#xff0c;出于学习的兴趣&#xff0c;我深入研究了一番&#xff0c;才有了这篇文章的诞生。 概念 那么&#xff0c;什么是DeepSeek&#xff1f;首先百…

minimind - 从零开始训练小型语言模型

大语言模型&#xff08;LLM&#xff09;领域&#xff0c;如 GPT、LLaMA、GLM 等&#xff0c;虽然它们效果惊艳&#xff0c; 但动辄10 Bilion庞大的模型参数个人设备显存远不够训练&#xff0c;甚至推理困难。 几乎所有人都不会只满足于用Lora等方案fine-tuing大模型学会一些新的…

【机器学习】自定义数据集 使用pytorch框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测,对预测结果计算精确度和召回率及F1分数

一、使用pytorch框架实现逻辑回归 1. 数据部分&#xff1a; 首先自定义了一个简单的数据集&#xff0c;特征 X 是 100 个随机样本&#xff0c;每个样本一个特征&#xff0c;目标值 y 基于线性关系并添加了噪声。将 numpy 数组转换为 PyTorch 张量&#xff0c;方便后续在模型中…

数据分析系列--④RapidMiner进行关联分析(案例)

一、核心概念 1.项集&#xff08;Itemset&#xff09; 2.规则&#xff08;Rule&#xff09; 3.支持度&#xff08;Support&#xff09; 3.1 支持度的定义 3.2 支持度的意义 3.3 支持度的应用 3.4 支持度的示例 3.5 支持度的调整 3.6 支持度与其他指标的关系 4.置信度&#xff0…

国产之光DeepSeek架构理解与应用分析

目录 初步探索DeepSeek的设计 一、核心架构设计 二、核心原理与优化 三、关键创新点 四、典型应用场景 五、与同类模型的对比优势 六、未来演进方向 从投入行业生产的角度看 一、DeepSeek的核心功能扩展 二、机械电子工程产业中的具体案例 1. 预测性维护&#xff08;Predictive…

Golang :用Redis构建高效灵活的应用程序

在当前的应用程序开发中&#xff0c;高效的数据存储和检索的必要性已经变得至关重要。Redis是一个快速的、开源的、内存中的数据结构存储&#xff0c;为各种应用场景提供了可靠的解决方案。在这个完整的指南中&#xff0c;我们将学习什么是Redis&#xff0c;通过Docker Compose…

基于互联网+智慧水务信息化整体解决方案

智慧水务的概述与发展背景 智慧水务是基于互联网、云计算、大数据、物联网等先进技术&#xff0c;对水务行业的工程建设、生产管理、管网运营、营销服务及企业综合管理等业务进行全面智慧化管理的创新模式。它旨在解决水务企业分散经营、管理水平不高、投资不足等问题。 水务…

力扣动态规划-16【算法学习day.110】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;建议灵神的题单和代码随想录&#xff09;和记录自己的学习过程&#xff0c;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关…

使用 Tauri 2 + Next.js 开发跨平台桌面应用实践:Singbox GUI 实践

Singbox GUI 实践 最近用 Tauri Next.js 做了个项目 - Singbox GUI&#xff0c;是个给 sing-box 用的图形界面工具。支持 Windows、Linux 和 macOS。作为第一次接触这两个框架的新手&#xff0c;感觉收获还蛮多的&#xff0c;今天来分享下开发过程中的一些经验~ 为啥要做这个…

langgraph实现 handsoff between agents 模式 (1)

官网示例代码 from typing_extensions import Literal from langchain_core.messages import ToolMessage from langchain_core.tools import tool from langgraph.graph import MessagesState, StateGraph, START from langgraph.types import Command from langchain_openai…