【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是否在另一个对象里,如果在,则判断两个对象相同key的value值是否相等
- 判断两个对象的长度是否相等(通过
// 对象浅比较的方法
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
在这个例子中,objB 有 name 和 age 这两个自有属性,因此 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 是从原型链继承来的,而 age 是 employee 自己定义的属性,所以返回 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,因为age是employee自有的属性。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 和 === 在大多数情况下行为相同,但是它们有两个显著的不同点:
NaN:在===中,NaN不等于NaN,但是在Object.is中,NaN等于NaN。+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; 的含义是判断 objA 和 objB 是否是相等的,并在它们相等时返回 true。这里使用的是严格相等运算符 ===。
-
===运算符:- 严格相等:
===是严格相等运算符,它会同时比较值和类型。如果两个操作数的类型不同,返回false。 - 对于引用类型(例如对象、数组、函数等),
===比较的是内存地址,即两个变量是否引用同一个对象。
- 严格相等:
-
对象的比较:
- 如果
objA和objB都是对象类型(包括数组、函数等),===会检查它们是否指向同一个对象。 - 如果它们指向同一个内存地址(即是同一个对象),则返回
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 的引用,指向同一个对象
相关文章:
【React】PureComponent 和 Component 的区别
前言 在 React 中,PureComponent 和 Component 都是用于创建组件的基类,但它们有一个主要的区别:PureComponent 会给类组件默认加一个shouldComponentUpdate周期函数。在此周期函数中,它对props 和 state (新老的属性/状态)会做一…...
MongoDb user自定义 role 添加 action(collStats, EstimateDocumentCount)
使用 mongosh cd mongsh_bin_path mongosh “mongodb://user:passip:port/db”这样就直接进入了对应的db 直接输入: 这样 role “read_only_role" 就获得了3个 action, 分别是 查询,列举集合,集合元数据查询 P.S: 如果没有 …...
fastadmin中require-form.js的data-favisible控制显示隐藏
只要在任意元素上添加data-favisible属性就可以轻松的控制显示隐藏了 其中reportype是php传到前端的一个变量??? <div class"form-group" data-favisible"reportype6"><label class"control-label col-xs-12 col-sm-2">{:__(Ove_…...
Day51:type()函数
在 Python 中,type() 是一个内置函数,用于返回对象的类型。它可以用于检查变量的类型,也可以用于动态创建新的类型。今天,我们将深入了解 type() 函数的使用方法。 1. 使用 type() 获取变量的类型 最常见的使用方式是将一个对象…...
vue 无法 局域网内访问
资料 Vue项目设置可以局域网访问_vue.js_脚本之家 过程 上午,前端vue服务能够在局域网内访问, 下午就不行了,但是后端服务能够正常访问,本机也能正常访问ip:端口号 前端服务 判定不是下面的问题: 同一…...
【llm对话系统】大模型 Llama 源码分析之 LoRA 微调
1. 引言 微调 (Fine-tuning) 是将预训练大模型 (LLM) 应用于下游任务的常用方法。然而,直接微调大模型的所有参数通常需要大量的计算资源和内存。LoRA (Low-Rank Adaptation) 是一种高效的微调方法,它通过引入少量可训练参数,固定预训练模型…...
蓝桥杯刷题DAY2:二维前缀和 一维前缀和 差分数组
闪耀的灯光 📌 题目描述 蓝桥公园是一个适合夜间散步的好地方,公园可以被视为由 n m 个矩形区域构成。每个区域都有一盏灯,初始亮度为 a[i][j]。 小蓝可以选择一个大的矩形区域,并按下开关一次,这将使得该区域内每盏…...
网件r7000刷回原厂固件合集测评
《网件R7000路由器刷回原厂固件详解》 网件R7000是一款备受赞誉的高性能无线路由器,其强大的性能和可定制性吸引了许多高级用户。然而,有时候用户可能会尝试第三方固件以提升功能或优化网络性能,但这也可能导致一些问题,如系统不…...
算法随笔_35: 每日温度
上一篇:算法随笔_34: 最后一个单词的长度-CSDN博客 题目描述如下: 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升…...
C++初阶 -- 手撕string类(模拟实现string类)
目录 一、string类的成员变量 二、构造函数 2.1 无参版本 2.2 有参版本 2.3 缺省值版本 三、析构函数 四、拷贝构造函数 五、c_str函数 六、operator重载 七、size函数 八、迭代器iterator 8.1 正常版本 8.2 const版本 九、operator[] 9.1 正常版本 9.2 const版…...
【Unity3D】实现2D角色/怪物死亡消散粒子效果
核心:这是一个Unity粒子系统自带的一种功能,可将粒子生成控制在一个Texture图片网格范围内,并且粒子颜色会自动采样图片的像素点颜色,之后则是粒子编辑出消散效果。 Particle System1物体(爆发式随机速度扩散10000个粒…...
构建一个数据分析Agent:提升分析效率的实践
在上一篇文章中,我们讨论了如何构建一个智能客服Agent。今天,我想分享另一个实际项目:如何构建一个数据分析Agent。这个项目源于我们一个金融客户的真实需求 - 提升数据分析效率,加快决策速度。 从分析师的痛点说起 记得和分析师团队交流时的场景: 小张ÿ…...
85.[1] 攻防世界 WEB easyphp
进入靶场 属于代码审计 <?php // 高亮显示当前 PHP 文件的源代码,常用于调试或展示代码 highlight_file(__FILE__);// 初始化两个标志变量,用于后续条件判断 $key1 0; $key2 0;// 从 GET 请求中获取参数 a 和 b $a $_GET[a]; $b $_GET[b];// 检…...
嵌入式硬件篇---CPUGPUTPU
文章目录 第一部分:处理器CPU(中央处理器)1.通用性2.核心数3.缓存4.指令集5.功耗和发热 GPU(图形处理器)1.并行处理2.核心数量3.内存带宽4.专门的应用 TPU(张量处理单元)1.为深度学习定制2.低精…...
pytorch图神经网络处理图结构数据
人工智能例子汇总:AI常见的算法和例子-CSDN博客 图神经网络(Graph Neural Networks,GNNs)是一类能够处理图结构数据的深度学习模型。图结构数据由节点(vertices)和边(edges)组成&a…...
海外问卷调查,最常用到的渠道查有什么特殊之处
市场调研,包含市场调查和市场研究两个步骤,是企业和机构根据经营方向而做出的决策问题,最终通过海外问卷调查中的渠道查,来系统地设计、收集、记录、整理、分析、研究市场反馈的工作流程。 市场调研的工作流程包括:确…...
【Uniapp-Vue3】解决uni-popup弹窗在安全区显示透明问题
我们在使用uni-popup时,如果想要给弹出内容添加一个背景颜色,我们会发现在安全区域是不显示该背景颜色的。 首先根据如下的目录结构找到uni-popup.vue文件 在该文件中找到bottom配置,将红箭头所指代码注释掉 下面的安全区域就没有了ÿ…...
什么是麦克斯韦方程
飞书链接,格式更好,⭐⭐⭐:长尾 - 什么是麦克斯韦方程 基于作者的内容,做了一些扩展(问了 DeepSeek) 最美的公式:你也能懂的麦克斯韦方程组(积分篇) 04通量的引入 用一个…...
webrtc peerconnection_client peerconnection_server 连接失败问题解决 win10 win11
0 常见问题 (1) webrtc peerconnection_client 连接 peerconnection_server 无连接列表 (2)连接导致崩溃debug状态下因为这个断言 RTC_DCHECK_RUN_ON(&capture_checker_); 1 在 peerconnection\client\main.cc 当中 定义类 class CustomSock…...
项目练习:重写若依后端报错cannot be cast to com.xxx.model.LoginUser
文章目录 一、情景说明二、解决办法 一、情景说明 在重写若依后端服务的过程中 使用了Redis存放LoginUser对象数据 那么,有存就有取 在取值的时候,报错 二、解决办法 方法1、在TokenService中修改如下 getLoginUser 方法中:LoginUser u…...
2025年2月1日(Keep calm and code Python)
“Keep calm and code Python” 的意思是 “保持冷静,编写 Python 代码”。 这句话来源于 “Keep Calm and Carry On”(保持冷静,继续前进),这是二战时期英国政府的宣传口号。后来,这种句式被广泛模仿&…...
核心集:DeepCore: A Comprehensive Library for CoresetSelection in Deep Learning
目录 一、TL;DR 二、为什么研究核心集? 三、问题定义和如何做 3.1 问题定义 3.2 业界方法 3.2.1 基于几何的方法 3.2.2 基于不确定性的方法 3.2.3 基于误差/损失的方法 3.2.5 GraNd 和 EL2N 分数 3.2.6 重要性采样 3.2.7 基于决策边界的办法 …...
Hot100之矩阵
73矩阵置零 题目 思路解析 收集0位置所在的行和列 然后该行全部初始化为0 该列全部初始化为0 代码 class Solution {public void setZeroes(int[][] matrix) {int m matrix.length;int n matrix[0].length;List<Integer> list1 new ArrayList<>();List<…...
可视化相机pose colmap形式的相机内参外参
目录 内参外参转换 可视化相机pose colmap形式的相机内参外参 内参外参转换 def visualize_cameras(cameras, images):fig plt.figure()ax fig.add_subplot(111, projection3d)for image_id, image_data in images.items():qvec image_data[qvec]tvec image_data[tvec]#…...
第十二章 I 开头的术语
文章目录 第十二章 I 开头的术语以 I 开头的术语被识别 (identified by)识别关系 (identifying relationship)身份 (identity)idkey隐式全局引用 (implicit global reference)隐含命名空间 (implied namespace)包含文件 (include file)传入锁 (incoming lock) 索引 (index)索引…...
C# 类与对象详解
.NET学习资料 .NET学习资料 .NET学习资料 在 C# 编程中,类与对象是面向对象编程的核心概念。它们让开发者能够将数据和操作数据的方法封装在一起,从而构建出模块化、可维护且易于扩展的程序。下面将详细介绍 C# 中类与对象的相关知识。 一、类的定义 …...
Windsurf cursor vscode+cline 与Python快速开发指南
Windsurf简介 Windsurf是由Codeium推出的全球首个基于AI Flow范式的智能IDE,它通过强大的AI助手功能,显著提升开发效率。Windsurf集成了先进的代码补全、智能重构、代码生成等功能,特别适合Python开发者使用。 Python环境配置 1. Conda安装…...
深度解析:网站快速收录与服务器性能的关系
本文转自:百万收录网 原文链接:https://www.baiwanshoulu.com/37.html 网站快速收录与服务器性能之间存在着密切的关系。服务器作为网站运行的基础设施,其性能直接影响到搜索引擎对网站的抓取效率和收录速度。以下是对这一关系的深度解析&am…...
Linux tr 命令使用详解
简介 tr (translate)命令用于在 Linux 中翻译或删除输入流(通常是 stdin )中的字符。它主要用于文本操作,并且可以作为转换或删除文本文件或流中的特定字符的方便工具。 基本语法 tr [OPTION] [SET1] [SET2]SET1&am…...
数据库内存与Buffer Pool
数据库内存与Buffer Pool 文章目录 数据库内存与Buffer Pool一:MySQL内存结构1:MySQL工作组件2:工作线程的本地内存3:共享内存区域4:存储引擎缓冲区 二:InnoDB的核心:Buffer Pool1:数…...
