淺談React 中的淺比較是如何工作的
React 中淺比較的概念無處不在,它在不同的流程中起著關(guān)鍵的作用,也可以在React組件的多個(gè)生命周期中找到。比如,React Hooks中的依賴數(shù)組,通過React.memo進(jìn)行記憶。在React的官方文檔中也多次提到“淺比較”這個(gè)概念,下面我們就來看看React中的淺比較是如何工作的!
想要理解淺比較的概念,最直接的方法就是研究React的源代碼,下面就來看看React中的shallowEqual.js 文件:
import is from './objectIs'; import hasOwnProperty from './hasOwnProperty'; /** ?* Performs equality by iterating through keys on an object and returning false ?* when any key has values which are not strictly equal between the arguments. ?* Returns true when the values of all keys are strictly equal. ?*/ function shallowEqual(objA: mixed, objB: mixed): boolean { ? if (is(objA, objB)) { ? ? return true; ? } ? if ( ? ? typeof objA !== 'object' || ? ? objA === null || ? ? typeof objB !== 'object' || ? ? objB === null ? ) { ? ? return false; ? } ? const keysA = Object.keys(objA); ? const keysB = Object.keys(objB); ? if (keysA.length !== keysB.length) { ? ? return false; ? } ?? ? // Test for A's keys different from B. ? for (let i = 0; i < keysA.length; i++) { ? ? const currentKey = keysA[i]; ? ? if ( ? ? ? !hasOwnProperty.call(objB, currentKey) || ? ? ? !is(objA[currentKey], objB[currentKey]) ? ? ) { ? ? ? return false; ? ? } ? } ? return true; }
這里執(zhí)行了很多步操作,下面就來將其拆分并逐步執(zhí)行這些功能。先來看看函數(shù)的定義,這個(gè)函數(shù)接受兩個(gè)需要比較的對(duì)象,這里的代碼使用 Flow 作為類型檢查系統(tǒng)。兩個(gè)函數(shù)參數(shù)都是使用特殊的混合 Flow 類型定義,類似于 TypeScript 的 unknown,它表明函數(shù)可以是任何類型的值。
function shallowEqual(objA: mixed, objB: mixed): boolean { // ... }
之后使用 React 內(nèi)部對(duì)象的 is 函數(shù)將兩個(gè)函數(shù)參數(shù)進(jìn)行比較。導(dǎo)入的 is 函數(shù)只不過是JavaScript 的 Object.is 函數(shù)的polyfill 版本。 這個(gè)比較函數(shù)基本上等同于常見的 === 運(yùn)算符,但有兩個(gè)例外:
- Object.is 認(rèn)為 +0 和 -0 不相等,而 === 認(rèn)為它們相等;
- Object.is 認(rèn)為 Number.NaN 和 NaN 相等,而 === 認(rèn)為它們不相等。
基本上,第一個(gè)條件語句可以處理所有簡(jiǎn)單的情況:如果兩個(gè)函數(shù)參數(shù)具有相同的值,對(duì)于原始類型,或引用相同的對(duì)象(數(shù)組和對(duì)象),那么通過淺比較認(rèn)為它們相等的。
import is from './objectIs'; function shallowEqual(objA: mixed, objB: mixed): boolean { ? if (is(objA, objB)) { ? ? return true; ? } ?? ?// ... }
在處理兩個(gè)函數(shù)參數(shù)值相等或者引用同一個(gè)對(duì)象的所有簡(jiǎn)單情況之后,來看看更復(fù)雜的結(jié)構(gòu):數(shù)組和對(duì)象。
為了確?,F(xiàn)在要處理的是兩個(gè)復(fù)雜的結(jié)構(gòu),代碼會(huì)檢查任一參數(shù)是不是object類型或者等于null,前者用來確保我們處理的數(shù)組或?qū)ο?,后者用來過濾掉空值,因?yàn)閠ypeof null的結(jié)果也是 object。如果任何一個(gè)條件成立,那兩個(gè)參數(shù)一定是不相等的(否則前面的條件語句就會(huì)將它們過濾掉),因此淺比較直接返回false。
function shallowEqual(objA: mixed, objB: mixed): boolean { ?? ?// ... ? if ( ? ? typeof objA !== 'object' || ? ? objA === null || ? ? typeof objB !== 'object' || ? ? objB === null ? ) { ? ? return false; ? } ?? ?// ... }
現(xiàn)在就可以確保我們處理的是數(shù)組和對(duì)象了,接下來我們深入研究復(fù)雜數(shù)據(jù)結(jié)構(gòu)的值,并在兩個(gè)函數(shù)參數(shù)之間進(jìn)行比較。在此之前,先來檢查兩個(gè)參數(shù)中值的數(shù)量是否相等,如果不相等,直接就可以確定兩個(gè)值是不相等的。對(duì)于對(duì)象,得到的keys數(shù)組就是由實(shí)際的key組成的;對(duì)于數(shù)組,得到keys數(shù)組數(shù)是由字符串類型的數(shù)組索引組成的。
function shallowEqual(objA: mixed, objB: mixed): boolean { ?? ?// ... ? const keysA = Object.keys(objA); ? const keysB = Object.keys(objB); ? if (keysA.length !== keysB.length) { ? ? return false; ? } ?? ?// ... }
最后一步,按照 key 來迭代兩個(gè)函數(shù)參數(shù)的值,并逐個(gè)驗(yàn)證他們是否是相等的。為此,代碼使用到了上一步中生成的keys數(shù)組,使用 hasOwnProperty 檢查key是否實(shí)際上是參數(shù)的屬性,并使用 Object.is 函數(shù)進(jìn)行比較。
import hasOwnProperty from './hasOwnProperty'; function shallowEqual(objA: mixed, objB: mixed): boolean { ?? ?// ... ? // Test for A's keys different from B. ? for (let i = 0; i < keysA.length; i++) { ? ? const currentKey = keysA[i]; ? ? if ( ? ? ? !hasOwnProperty.call(objB, currentKey) || ? ? ? !is(objA[currentKey], objB[currentKey]) ? ? ) { ? ? ? return false; ? ? } ? } ? return true; }
如果任何兩個(gè)key對(duì)應(yīng)的值是不相等的,那兩個(gè)對(duì)象肯定就是不相等的,因此直接人會(huì)false,結(jié)束循環(huán)。如果所有的值都是相等的,就返回 true。
至此,我們通過 React 源碼學(xué)習(xí)了 React 中的淺比較,下面來總結(jié)一下其中有趣的知識(shí)吧:
- 淺比較使用的是 Object.is 函數(shù),而不是使用嚴(yán)格相等 === 運(yùn)算符;
- 通過淺比較,空對(duì)象和空數(shù)組是等價(jià)的;
- 通過淺比較,以數(shù)組索引為 key 和數(shù)組值為value的對(duì)象是等價(jià)的,比如:{ 0: 2, 1: 3 } 等價(jià)于 [2, 3];
- 由于通過Object.is比較的+0 和 -0、Number.NaN 和 NaN是不相等的,所以在復(fù)雜結(jié)構(gòu)中比較時(shí),這也是適用的;
- 雖然{} 和 [] 錢比較是相等的,但是嵌套在對(duì)象中對(duì)象是不相等的,比如:{ someKey: {} } 和 { someKey: [] } 是不相等的。
源碼:https://github.com/facebook/react/blob/main/packages/shared/shallowEqual.js
到此這篇關(guān)于淺談React 中的淺比較是如何工作的的文章就介紹到這了,更多相關(guān)React 淺比較內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React Navigation 使用中遇到的問題小結(jié)
本篇文章主要介紹了React Navigation 使用中遇到的問題小結(jié),主要是安卓和iOS中相對(duì)不協(xié)調(diào)的地方,特此記錄,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05React Native實(shí)現(xiàn)進(jìn)度條彈框的示例代碼
本篇文章主要介紹了React Native實(shí)現(xiàn)進(jìn)度條彈框的示例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07react的ui庫antd中form表單使用SelectTree反顯問題及解決
這篇文章主要介紹了react的ui庫antd中form表單使用SelectTree反顯問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01React實(shí)現(xiàn)監(jiān)聽粘貼事件并獲取粘貼板中的截圖
這篇文章主要介紹了React實(shí)現(xiàn)監(jiān)聽粘貼事件并獲取粘貼板中的截圖方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08React 事件綁定的實(shí)現(xiàn)及區(qū)別
事件綁定也是其中一部分內(nèi)容,通過事件委托和事件合成,React 在內(nèi)部對(duì)事件進(jìn)行優(yōu)化和處理,減少了事件處理函數(shù)的調(diào)用次數(shù),從而提升了性能,本文主要介紹了React事件綁定的實(shí)現(xiàn)及區(qū)別,感興趣的可以了解一下2024-03-03淺談對(duì)于react-thunk中間件的簡(jiǎn)單理解
這篇文章主要介紹了淺談對(duì)于react-thunk中間件的簡(jiǎn)單理解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05