lodash已死?radash最全使用介紹(附源碼詳細說明)—— Array方法篇(1)
- 相信很多前端同學甚至非前端都或多或少使用過lodash庫,我們都知道lodash是一個非常豐富的前端工具庫,比如最常用的防抖和節流,使用lodash都能很快實現,在github上更是有著58.7k的star數。但最近出現的Radash庫,號稱lodash plus版本,比之更新、更小、更全面、源碼更易于理解。
- 閱讀本文你能了解些什么?
- radash是什么;
- 它相較于lodash有哪些優勢;
- radash 數組相關方法介紹及源碼解析。
認識Radash
一句話介紹:radash是一個強大的零依賴的前端工具庫。如果你會使用lodash,那么你使用radash將沒有任何門檻。
使用Radash有哪些優勢?
- 零依賴,radash不依賴任何第三方庫,僅在自己的源碼里面去實現功能,所以非常的輕量。使用它你只需要加載radash本身;
- Typescript編寫,使用起來更安全,不用擔心變量類型問題;
- 全面支持es6+的新特性。它去除了lodash身上一些過時的方法(這些方法能夠使用es6+新特性快速簡單實現);
- 方法更全面。包含數組相關、對象相關、排序相關、字符串相關、優化相關等等等等...,幾乎能滿足你能想到的前端工具方法。
- 源碼更易于理解。我們甚至可以說radash的某些方法的實現時直接而暴力的(這點你會在我后續的方法源碼介紹中有所感受)。
Radash相關方法如何使用
-
下載radash
npm install radash --save // 或 yarn下載 yarn add radash -
引入你需要的方法
import { alphabetical } from 'radash' -
按照要求傳入相關參數就可以使用了。
Radash的數組相關操作方法詳解
注意:以下我們示例將直接使用,不再進行引入操作,實際使用時記得先引入再使用
alphabetical:把對象數組按照選定key的value的字母順序排列
-
用法說明
- 參數:目標對象數組、用于排序的屬性的回調函數、第三個參數可選(不傳是升序排序,傳入
desc字符則表示降序排序); - 返回值:排序后的數組。
- 參數:目標對象數組、用于排序的屬性的回調函數、第三個參數可選(不傳是升序排序,傳入
-
基礎使用代碼示例
const ig = [ { name: 'ning', power: 100 }, { name: 'rookie', power: 98 }, { name: 'jkl', power: 95 }, { name: 'theshy', power: 100 } ] // 這里輸出的依然是對象數組,這里簡單表示 alphabetical(ig, g => g.name) // => [jkl, ning, rookie, theshy] alphabetical(ig, g => g.name, 'desc') // => [theshy, rookie, ning, jkl] -
源碼解析
// 定義一個泛型函數 `alphabetical`,接受一個泛型數組 `array`, // 一個用于從數組項中獲取排序依據字符串的函數 `getter`, // 和一個可選的方向參數 `dir`,默認值為 'asc'(升序)。 export const alphabetical = <T>( array: readonly T[], getter: (item: T) => string, dir: 'asc' | 'desc' = 'asc' ) => { // 如果輸入數組不存在或為空,直接返回一個空數組 if (!array) return [] // 定義一個升序比較函數,使用 `localeCompare` 方法比較通過 `getter` 獲取的字符串。 const asc = (a: T, b: T) => `${getter(a)}`.localeCompare(getter(b)) // 定義一個降序比較函數,它將通過 `getter` 獲取的字符串逆序比較。 const dsc = (a: T, b: T) => `${getter(b)}`.localeCompare(getter(a)) // 使用 `slice` 方法克隆數組,避免修改原數組,然后根據 `dir` 參數選擇排序函數進行排序。 return array.slice().sort(dir === 'desc' ? dsc : asc) }- 方法工作流程說明:
-
這個函數的作用是對任何類型的數組進行排序,排序依據是數組每項通過
getter函數得到的字符串。調用localeCompare是為了正確地比較可能包含特殊字符的字符串。這個函數還允許用戶指定排序方向,升序或降序; -
localeCompare是一個字符串方法,用于比較兩個字符串,并返回一個表示這兩個字符串在排序中相對位置的數字。該方法基于本地語言環境的排序規則進行比較,這意味著它可以正確地比較具有特定語言字符和變音符號的字符串。
當localeCompare被調用時,它將返回三種可能的值:- 如果字符串在排序中應該出現在比較字符串之前,則返回一個負數;
- 如果兩個字符串相等(在排序中的位置相同),則返回 0;
- 如果字符串在排序中應該出現在比較字符串之后,則返回一個正數;
例如,利用
localeCompare方法可以正確地對包含德語、法語或西班牙語等特殊字符的字符串進行排序,而不僅僅是基于ASCII碼值的簡單比較。
-
- 方法工作流程說明:
boil:返回對象數組中滿足條件的對象
-
用法說明
- 參數:目標對象數組、條件函數;
- 返回值:滿足條件的對象。
-
基礎代碼示例
const rng = [ { name: 'Uzi', power: 100 }, { name: 'Xiaohu', power: 98 }, { name: 'Ming', power: 72 } ] boil(gods, (a, b) => (a.power > b.power ? a : b)) // => { name: 'Uzi', power: 100 } boil(gods, (a, b) => (a.power < b.power ? a : b)) // => { name: 'Ming', power: 72 } -
源碼解析
// 定義一個泛型函數 `boil`,它接受一個具有只讀屬性的泛型數組 `array`, // 以及一個用于比較數組中兩個元素并返回其中一個的比較函數 `compareFunc`。 export const boil = <T>( array: readonly T[], compareFunc: (a: T, b: T) => T ) => { // 如果傳入的數組不存在或長度為0,則函數返回 null。 if (!array || (array.length ?? 0) === 0) return null // 使用數組的 `reduce` 方法應用 `compareFunc`,將數組歸約為單一的值。 return array.reduce(compareFunc) }-
方法工作流程說明:
在這個函數中,
reduce方法接收compareFunc作為參數。reduce方法會遍歷數組的所有元素,并且在每一步中應用compareFunc,將數組中的元素逐漸歸約到一個單一的結果。compareFunc函數負責決定如何從兩個元素中選擇一個。
-
cluster:把一個數組盡量均勻的分成多個數組
- 用法說明
- 參數:目標數組、分組個數n;
- 返回值:分組后的二維數組。
- 基礎代碼示例
const gods = ['Ra', 'Zeus', 'Loki', 'Vishnu', 'Icarus', 'Osiris', 'Thor', 'Apollo', 'Artemis', 'Athena'] cluster(gods, 3) // => [ // [ 'Ra', 'Zeus', 'Loki' ], // [ 'Vishnu', 'Icarus', 'Osiris' ], // ['Thor', 'Apollo', 'Artemis'], // ['Athena'] // ] - 源碼解析
// 定義一個泛型函數 `cluster`,它接收一個具有只讀屬性的泛型數組 `list`, // 以及一個可選的數字參數 `size`,默認值為2,表示子數組的大小。 export const cluster = <T>(list: readonly T[], size: number = 2): T[][] => { // 計算出需要多少個子數組群組來容納原數組,確保即使不能完全平分也會創建一個額外的群組來容納剩余的元素。 const clusterCount = Math.ceil(list.length / size) // 創建一個新數組,長度為 `clusterCount`,初始填充為 `null`。 return new Array(clusterCount).fill(null).map((_c: null, i: number) => { // 對于新數組中的每個元素,使用 `slice` 方法從原數組 `list` 中提取出相應的子數組。 // 子數組的開始索引是 `i * size`,結束索引是 `i * size + size`。 return list.slice(i * size, i * size + size) }) }- 方法工作流程說明:
- 首先,使用
Math.ceil函數計算出給定數組大小和子數組大小的情況下,需要多少個子數組群組。因為Math.ceil向上取整,這確保了即使最后一個群組不滿也會被創建; - 接著,創建一個新的數組,這個數組的長度是我們剛才計算出的群組數量
clusterCount。使用fill(null)方法將其填充為null,這樣我們就可以在其上使用map方法; - 然后,對這個新數組使用
map方法,對于其中的每個元素(最初都是null),我們計算原數組list中對應的子數組應該從哪里開始(i * size),到哪里結束(i * size + size),并使用slice方法提取這個子數組; - 最終,我們得到一個新的數組,它由原數組
list切分成多個子數組組成,每個子數組的最大長度由size參數決定。
- 首先,使用
- 方法工作流程說明:
counting:統計對象數組中每個唯一標識符的出現次數
- 用法說明
- 參數:目標對象數組、條件函數(內部是傳入目標對象,返回對象身上的某一項——根據這項來做統計);
- 返回值:統計對象。
- 基礎代碼示例
const skt = [ { name: 'Ra', culture: 'egypt' }, { name: 'Zeus', culture: 'greek' }, { name: 'Loki', culture: 'greek' } ] counting(gods, g => g.culture) // => { egypt: 1, greek: 2 } - 源碼解析
// 定義一個泛型函數 `counting`,它接收一個具有只讀屬性的泛型數組 `list`, // 和一個函數 `identity`,該函數用于從數組每個元素中提取一個唯一標識符(可以是字符串、數字或符號)。 export const counting = <T, TId extends string | number | symbol>( list: readonly T[], identity: (item: T) => TId ): Record<TId, number> => { // 如果傳入的數組不存在,則返回一個空對象。 if (!list) return {} as Record<TId, number> // 使用數組的 `reduce` 方法來累計每個唯一標識符的出現次數。 return list.reduce((acc, item) => { // 使用 `identity` 函數從當前元素 `item` 中獲取唯一標識符。 const id = identity(item) // 如果 `acc`(累加器)中已經有這個標識符的記錄,則增加它的計數,否則初始化為1。 acc[id] = (acc[id] ?? 0) + 1 // 返回更新后的累加器對象。 return acc }, {} as Record<TId, number>) // 初始化累加器為一個空對象。 }- 方法工作流程說明:
- 接收一個數組
list和一個identity函數,后者用于指定如何從數組項中提取唯一標識符; - 如果傳入的
list為空,返回一個空的記錄對象; - 使用
reduce方法遍歷數組。reduce的累加器acc是一個對象,其鍵是通過identity函數從數組項中提取的唯一標識符,值是標識符出現的次數; - 在每次迭代中,從當前項
item中提取唯一標識符id。如果acc中已經存在id鍵,就將其值加1;如果不存在,就將其值設置為1; - 最終,返回這個累加器對象,它是一個記錄對象,其鍵是唯一標識符,值是對應的出現次數。
- 接收一個數組
- 方法工作流程說明:
diff:返回數組1中出現但是沒在數組2中出現的項
- 用法說明
- 參數:目標數組1、目標數組2;
- 返回值:包含符合項的數組。
- 基礎代碼示例
import { diff } from 'radash' const oldWorldGods = ['rng', 'uzi'] const newWorldGods = ['vishnu', 'uzi'] diff(oldWorldGods, newWorldGods) // => ['rng'] - 源碼解析
// 定義一個泛型函數 `diff`,它接收兩個具有只讀屬性的泛型數組 `root` 和 `other`, // 以及一個可選的函數 `identity`,用于從數組元素中提取一個唯一標識符(默認為將元素直接作為標識符)。 export const diff = <T>( root: readonly T[], other: readonly T[], identity: (item: T) => string | number | symbol = (t: T) => t as unknown as string | number | symbol ): T[] => { // 如果兩個數組都為空或未定義,則返回一個空數組。 if (!root?.length && !other?.length) return [] // 如果 `root` 數組未定義或為空,則返回 `other` 數組的副本。 if (root?.length === undefined) return [...other] // 如果 `other` 數組未定義或為空,則返回 `root` 數組的副本。 if (!other?.length) return [...root] // 使用 `other` 數組的元素創建一個記錄對象 `bKeys`,鍵是通過 `identity` 函數提取的唯一標識符,值為 `true`。 const bKeys = other.reduce((acc, item) => { acc[identity(item)] = true return acc }, {} as Record<string | number | symbol, boolean>) // 過濾 `root` 數組,只返回不在 `bKeys` 記錄對象中的元素。 return root.filter(a => !bKeys[identity(a)]) }-
方法工作流程說明:
- 檢查
root和other數組是否都為空或未定義,如果是,則返回空數組; - 如果
root數組為空或未定義,而other數組不是,返回other數組的副本; - 如果
other數組為空或未定義,而root數組不是,返回root數組的副本; - 如果兩個數組都不為空,使用
other數組的元素創建一個記錄對象bKeys。identity函數用于為每個元素提取唯一標識符,這些標識符作為bKeys對象的鍵,其對應的值被設置為true; - 使用
filter方法遍歷root數組,返回那些其通過identity函數提取的唯一標識符不在bKeys對象中的元素。這些元素構成了root和other數組的差異集。
- 檢查
-
下期我們將介紹以下方法
提示:如果是簡單使用的話可以直接按照介紹選擇合適的方法進行使用,我們后續會詳細介紹。
- first:獲取數組第一項,不存在返回默認值;
- flat:數組扁平化 —— 把多維數組轉為一維數組;
- fork:按條件將數組拆分成兩個數組,滿足條件的一個,不滿足條件的一個;
- group:根據條件函數指定的唯一標識符出現次數對數組進行排序;
- intersects:判斷兩個數組是否有公共項,返回一個布爾值。
寫在后面
后續作者會整理一份方法目錄上傳,方便沒法訪問外網的同學對照查看使用。
大家有任何問題或者見解,歡迎評論區留言交流!!!
點擊訪問:Radash官網

浙公網安備 33010602011771號