- 相信很多前端同學甚至非前端都或多或少使用過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官網
參考文章:Lodash is dead. Long live Radash.