【React】PureComponent 和 Component 的区别

devtools/2025/2/2 13:54:07/

前言

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

一、shouldComponentUpdate 使用

javascript"> 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
javascript">const isObject = function isObject(obj){return obj !== null && /^(object|function)$/.test(typeof obj);
}

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

  • 判断传过的对象是否为对象
  • 判断对象是否相等
  • 接下来就开始判断两个对象的key、value
    • 判断两个对象的长度是否相等(通过Reflect.ownkeys(obj)获取key)
    • 循环其中一个对象的key集合,判断该对象的key是否在另一个对象里,如果在,则判断两个对象相同keyvalue 值是否相等
javascript">
// 对象浅比较的方法
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,且手动判断属性和状态是否改变。

javascript">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还是之前的状态地址。

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

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

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

javascript"><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

javascript">       <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 设置为不可枚举的属性)和符号属性都不会被返回。

示例:

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

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

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

示例:

javascript">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() 使用:

javascript">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 的一个原型方法,属于所有对象实例。它用于检查给定的属性是否是对象自身的直接属性,而不是从原型链继承来的属性。

语法

javascript">obj.hasOwnProperty(key)

示例 1:检查自有属性

javascript">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:检查继承的属性

javascript">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:区别

javascript">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:避免属性冲突

javascript">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 比较

javascript">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 比较

javascript">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

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

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

javascript">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

示例:

javascript">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.ppmy.cn/devtools/155457.html

相关文章

什么是Javascript,有什么特点

Javascript JavaScript 是一种广泛使用的编程语言&#xff0c;主要用于在网页上添加交互性和动态功能。 它最初由 Netscape 公司的 Brendan Eich 在 1995 年开发&#xff0c;并迅速成为 Web 开发的标准之一。 主要特点 解释型语言&#xff1a; JavaScript 是一种解释型语言&…

基于SpringBoot+WebSocket的前后端连接,并接入文心一言大模型API

前言&#xff1a; 本片博客只讲述了操作的大致流程&#xff0c;具体实现步骤并不标准&#xff0c;请以参考为准。 本文前提&#xff1a;熟悉使用webSocket 如果大家还不了解什么是WebSocket&#xff0c;可以参考我的这篇博客&#xff1a; rWebSocket 详解&#xff1a;全双工…

11 Spark面试真题

11 Spark大厂面试真题 1. 通常来说&#xff0c;Spark与MapReduce相比&#xff0c;Spark运行效率更高。请说明效率更高来源于Spark内置的哪些机制&#xff1f;2. hadoop和spark使用场景&#xff1f;3. spark如何保证宕机迅速恢复?4. hadoop和spark的相同点和不同点&#xff1f;…

LeetCode--84. 柱状图中最大的矩形【单调栈】

84. 柱状图中最大的矩形 正文 题目如下 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 这道题暴力很简单&#xff0c;但是时间复杂度是O(N^2)&#xf…

手写MVVM框架-环境搭建

项目使用 webpack 进行进行构建&#xff0c;初始化步骤如下: 1.创建npm项目执行npm init 一直下一步就行 2.安装webpack、webpack-cli、webpack-dev-server&#xff0c;html-webpack-plugin npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin 3.配置webpac…

OpenCV:特征检测总结

目录 一、什么是特征检测&#xff1f; 二、OpenCV 中的常见特征检测方法 1. Harris 角点检测 2. Shi-Tomasi 角点检测 3. Canny 边缘检测 4. SIFT&#xff08;尺度不变特征变换&#xff09; 5. ORB 三、特征检测的应用场景 1. 图像匹配 2. 运动检测 3. 自动驾驶 4.…

人工智能入门课【手写自注意力机制】

原理 自注意力&#xff08;Self-Attention&#xff09;是一种强大的机制&#xff0c;广泛应用于自然语言处理、计算机视觉等领域&#xff0c;尤其是在Transformer架构中发挥了关键作用。它的核心思想是让模型能够动态地关注输入序列中不同位置之间的关系&#xff0c;从而更好地…

AI大模型开发原理篇-4:神经概率语言模型NPLM

神经概率语言模型&#xff08;NPLM&#xff09;概述 神经概率语言模型&#xff08;Neural Probabilistic Language Model, NPLM&#xff09; 是一种基于神经网络的语言建模方法&#xff0c;它将传统的语言模型和神经网络结合在一起&#xff0c;能够更好地捕捉语言中的复杂规律…