马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
媒介
在 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->0x001
- return <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堆中新增一个40
- this.setState({ arr }); //最新修改的转态地址,还是0x001「状态地址没有改」
- }}>新增SPAN</button>
复制代码 此时想要页面状态改变则有两种方式:
(1)方式一:利用this.forceUpdate(),直接跳过shouldComponentUpdate周期,以是页面肯定会更改状态并渲染- <button onClick={() => {
- arr.push(40); //给0x001堆中新增一个40
- this.setState({ arr }); //最新修改的转态地址,还是0x001「状态地址没有改」
- this.forceUpdate(); //跳过默认加的shouldComponentUpdate,直接更新
- }}>新增SPAN</button>
复制代码 (2)方式二:修改arr的状态地点
利用this.setState({ arr: [...arr] }),此时arr的引用地点就不是之前的引用地点,是一个全新的数组,则objA === objB就为false。- <button onClick={() => {
- arr.push(40); //给0x001堆中新增一个40
- this.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 的一个原型方法,属于全部对象实例。它用于查抄给定的属性是否是对象自身的直接属性,而不是从原型链继续来的属性。
语法:示例 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 的引用,指向同一个对象
复制代码 |