React 使用了 PureComponent,在 shouldComponentUpdate
进行浅比较,这里到底什么是浅比较呢?javascript
在React里,shouldComponentUpdate源码为:java
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState);
}
复制代码
能够看到PureComponent就是对props跟state的先后状态作了一个浅比较。函数
看看shallowEqual的源码:ui
const hasOwn = Object.prototype.hasOwnPropertyfunctionis(x, y) {
if(x === y) {
returnx !==0 || y !==0 ||1/ x ===1/ y
} else {
returnx !== x && y !== y
}
}
export default function shallowEqual (objA, objB) {
if ( is(objA, objB ))
return true
i f( typeofobjA !=='object' || objA === null || typeofobjB !=='object' || objB ===null) {
return false
}
const keysA = Object.keys ( objA )
const keysB = Object.keys ( objB )
if ( keysA.length !== keysB.length)
return false
for ( leti =0; i < keysA.length; i++) {
if ( !hasOwn.call ( objB, keysA[i] ) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false
}
}
return true
}
复制代码
在解析 shallowEqual
的源码以前,先来认识一下 Object.is()
,这个函数是用来比较两个值是否相等。this
为何要用这个来比较而不是 ==
或者 ===
呢?spa
==
首先先看 ==
,因为 JS 是弱类型的,若是使用 ==
进行比较,==
操做符会自动将 0,‘’(空字符串),null,undefined
转成布尔型 false
,这样就会出现prototype
0==' '// true
null==undefined// true
[1] ==true// true
复制代码
这显然是不符合预期的。因此 JS 为咱们提供了全等操做符 ===
,它不会进行类型转换,也就是说若是两个值同样,必须符合类型也同样。可是,它仍是有两种疏漏的状况code
+0 === -0//true,但咱们期待它返回falseNaN===NaN//false,咱们期待它返回true
复制代码
因此,Object.is()
修复了 `=== 这两种判断不符合预期的状况,对象
function (x, y){
// SameValue algorithm
if(x === y) {
// 处理为+0 != -0的状况
returnx !==0 || 1/ x ===1/ y;
} else {
// 处理 NaN === NaN的状况
return x !== x && y !== y;
}
};
复制代码
这样就使得 Object.is()
老是返回咱们须要的结果。 它在下面6种状况下,会返回 true
ip
undefined
null
true
或者都是 false
+0
-0
NaN
NaN
外的其它同一个数字能够看出Object.is
能够对基本数据类型: null,undefined,number,string,boolean
作出很是精确的比较,可是对于引用数据类型是没办法直接比较的。
shallowEquall
// 用原型链的方法
const hasOwn = Object.prototype.hasOwnProperty
// 这个函数其实是 Object.is() 的 polyfill
functionis (x, y) {
if (x === y) {
returnx !==0 || y !==0 ||1/ x === 1/ y
} else {
returnx !== x && y !== y
}
}
export default function shallowEqual (objA, objB) {
// 首先对基本数据类型的比较
if (is(objA, objB))
return true
// 因为 Obejct.is() 能够对基本数据类型作一个精确的比较, 因此若是不等
// 只有一种状况是误判的,那就是 object, 因此在判断两个对象都不是 object
// 以后,就能够返回 false 了
if (typeofobjA !== 'object' || objA === null || typeofobjB !== 'object' || objB ===null ) {
return false
}
// 过滤掉基本数据类型以后,就是对对象的比较了
// 首先拿出 key 值,对 key 的长度进行对比
const keysA = Object.keys( objA)
const keysB = Object.keys(objB)
// 长度不等直接返回 false
if (keysA.length !== keysB.length)
return false
// key 相等的状况下,在去循环比较
for (let i =0; i < keysA.length; i++) {
// key值相等的时候
// 借用原型链上真正的 hasOwnProperty 方法,判断ObjB里面是否有A的key的key值
// 属性的顺序不影响结果也就是 {name:'daisy', age:'24'} 跟 {age:'24',name:'daisy' } 是同样的
// 最后,对对象的value进行一个基本数据类型的比较,返回结果
if( !hasOwn.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])){
return false
}
}
return true
}
复制代码
回到最开始的问题,浅比较为何没办法对嵌套的对象比较?
由上面的分析能够看到,当对比的类型为Object的时候而且key的长度相等的时候,浅比较也仅仅是用Object.is()对Object的value作了一个基本数据类型的比较,因此若是key里面是对象的话,有可能出现比较不符合预期的状况,因此浅比较是不适用于嵌套类型的比较的。