背景
React 使用了 PureComponent,在 shouldComponentUpdate
進(jìn)行淺比較瞳抓,這里到底什么是淺比較呢长赞?
shallowEqual
在React里,shouldComponentUpdate源碼為:
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState);
}
可以看到PureComponent就是對props跟state的前后狀態(tài)做了一個淺比較惠爽。
看看shallowEqual的源碼:
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
}
Object.is()
在解析 shallowEqual
的源碼之前添吗,先來認(rèn)識一下 Object.is()
沥曹,這個函數(shù)是用來比較兩個值是否相等。
為什么要用這個來比較而不是 ==
或者 ===
呢根资?
==
首先先看 ==
架专,由于 JS 是弱類型的,如果使用 ==
進(jìn)行比較玄帕,==
操作符會自動將 0部脚,‘’(空字符串),null裤纹,undefined
轉(zhuǎn)成布爾型 false
委刘,這樣就會出現(xiàn)
0==' '// true
null==undefined// true
[1] ==true// true
這顯然是不符合預(yù)期的丧没。所以 JS 為我們提供了全等操作符 ===
,它不會進(jìn)行類型轉(zhuǎn)換锡移,也就是說如果兩個值一樣呕童,必須符合類型也一樣。但是淆珊,它還是有兩種疏漏的情況
+0 === -0//true夺饲,但我們期待它返回falseNaN===NaN//false,我們期待它返回true
所以施符,Object.is()
修復(fù)了 `=== 這兩種判斷不符合預(yù)期的情況往声,
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()
總是返回我們需要的結(jié)果。 它在下面6種情況下戳吝,會返回 true
- 兩個值都是
undefined
- 兩個值都是
null
- 兩個值都是
true
或者都是false
- 兩個值是由相同個數(shù)的字符按照相同的順序組成的字符串
- 兩個值指向同一個對象
- 兩個值都是數(shù)字并且
- 都是正零
+0
- 都是負(fù)零
-0
- 都是正零
- 都是
NaN
- 都是除零和
NaN
外的其它同一個數(shù)字
可以看出Object.is
可以對基本數(shù)據(jù)類型: null,undefined,number,string,boolean
做出非常精確的比較浩销,但是對于引用數(shù)據(jù)類型是沒辦法直接比較的。
剖析 shallowEquall
// 用原型鏈的方法
const hasOwn = Object.prototype.hasOwnProperty
// 這個函數(shù)實際上是 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) {
// 首先對基本數(shù)據(jù)類型的比較
if (is(objA, objB))
return true
// 由于 Obejct.is() 可以對基本數(shù)據(jù)類型做一個精確的比較听哭, 所以如果不等
// 只有一種情況是誤判的慢洋,那就是 object, 所以在判斷兩個對象都不是 object
// 之后,就可以返回 false 了
if (typeofobjA !== 'object' || objA === null || typeofobjB !== 'object' || objB ===null ) {
return false
}
// 過濾掉基本數(shù)據(jù)類型之后陆盘,就是對對象的比較了
// 首先拿出 key 值普筹,對 key 的長度進(jìn)行對比
const keysA = Object.keys( objA)
const keysB = Object.keys(objB)
// 長度不等直接返回 false
if (keysA.length !== keysB.length)
return false
// key 相等的情況下,在去循環(huán)比較
for (let i =0; i < keysA.length; i++) {
// key值相等的時候
// 借用原型鏈上真正的 hasOwnProperty 方法礁遣,判斷ObjB里面是否有A的key的key值
// 屬性的順序不影響結(jié)果也就是 {name:'daisy', age:'24'} 跟 {age:'24'斑芜,name:'daisy' } 是一樣的
// 最后肩刃,對對象的value進(jìn)行一個基本數(shù)據(jù)類型的比較祟霍,返回結(jié)果
if( !hasOwn.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])){
return false
}
}
return true
}
總結(jié)
回到最開始的問題,淺比較為什么沒辦法對嵌套的對象比較盈包?
由上面的分析可以看到沸呐,當(dāng)對比的類型為Object的時候并且key的長度相等的時候,淺比較也僅僅是用Object.is()對Object的value做了一個基本數(shù)據(jù)類型的比較呢燥,所以如果key里面是對象的話崭添,有可能出現(xiàn)比較不符合預(yù)期的情況,所以淺比較是不適用于嵌套類型的比較的叛氨。