寫在前面
tips:點贊 + 收藏 = 學會!
- 主頁有更多其他篇章的方法,歡迎訪問檢視。
- 本篇我們繼續介紹
radash
中函式柯里化和Number
相關的方法使用和原始碼解析。
函式柯里化
chain:建立一個函式鏈並依次執行
- 使用說明
- 功能描述:用於建立一個函式鏈,該鏈依次執行一系列函式,每個函式的輸出都是下一個函式的輸入。這種模式常見於函數語言程式設計,特別是在資料轉換和流水線處理中。
- 引數:函式陣列(或者說任意數量的函式)。
- 返回值:返回一個新的函式。
- 使用程式碼示例
import { chain } from 'radash' const add = (y: number) => (x: number) => x + y const mult = (y: number) => (x: number) => x * y const addFive = add(5) const double = mult(2) const chained = chain(addFive, double) chained(0) // => 10 chained(7) // => 24
- 原始碼解析
// 定義一個名為 `chain` 的函式。 export function chain( // `funcs` 是一個由函式組成的陣列,這些函式將會被依次執行。 // 每個函式都可以接受任意引數,並返回任何型別的值。 ...funcs: ((...args: any[]) => any)[] ) { // `chain` 函式返回一個新的函式,這個新函式接受任意引數。 return (...args: any[]) => { // 使用 `funcs` 陣列中的第一個函式和傳入的引數 `args` 來初始化累加器 `acc`。 // 然後,使用 `reduce` 方法依次執行剩餘的函式。 return funcs.slice(1).reduce( // 在每個迭代中,將前一個函式的返回值作為下一個函式的輸入。 (acc, fn) => fn(acc), // 第一次迭代的初始值是 `funcs` 陣列中第一個函式的執行結果。 funcs[0](...args) ) } }
- 方法流程說明:
chain
函式接受任意數量的函式作為引數,並返回一個新的函式。- 當新函式被呼叫時,它首先使用傳入的引數呼叫
funcs
陣列中的第一個函式。 - 然後,它使用
reduce
方法依次執行funcs
陣列中剩餘的函式,每個函式的返回值都被傳遞給下一個函式。 - 最終,返回最後一個函式的執行結果。
- 方法流程說明:
compose:依次執行傳入的函式,每個函式的輸出作為下一個函式的輸入
- 使用說明
- 功能描述:接受一系列函式作為引數,並返回一個新的函式。這個新函式將從右到左依次執行傳入的函式,每個函式的輸出作為下一個函式的輸入。這是函數語言程式設計中的組合(composition)模式。
- 引數:一系列函式(函式陣列)。
- 返回值:一個新的函式。
- 使用程式碼示例
import { compose } from 'radash' const useZero = (fn: any) => () => fn(0) const objectize = (fn: any) => (num: any) => fn({ num }) const increment = (fn: any) => ({ num }: any) => fn({ num: num + 1 }) const returnArg = (arg: any) => (args: any) => args[arg] const composed = compose( useZero, objectize, increment, increment, returnArg('num') ) composed() // => 2
- 原始碼解析
// 定義一個名為 `compose` 的函式。 export function compose( // 使用展開運算子接收一個函式陣列,每個函式可以接受任意引數並返回任何型別的值。 ...funcs: ((...args: any[]) => any)[] ) { // `compose` 函式返回一個新的函式。 return (...args: any[]) => { // 首先反轉 `funcs` 陣列,因為我們需要從右到左執行函式。 // 然後使用 `reduce` 方法來組合這些函式。 return funcs.reverse().reduce((acc, fn) => { // 在每次迭代中,將上一個函式的返回值(acc)作為當前函式(fn)的引數。 return fn(acc); }, args); // 初始值 `args` 是傳遞給組合函式的引數。 }; }
- 方法流程說明:
- 接受任意數量的函式作為引數。
- 返回一個新的函式,這個新函式接受任意引數。
- 當新函式被呼叫時,它首先反轉
funcs
陣列,以確保函式能夠從右到左執行。 - 使用
reduce
方法應用每個函式,從最右邊的函式開始,每個函式的返回值都作為下一個函式的輸入。 - 最終,返回最左邊函式的執行結果。
- 方法流程說明:
debounce:函式防抖
- 使用說明
- 功能描述:建立一個防抖函式,該函式會在指定的延遲時間後執行。防抖(debouncing)是一種控制函式執行頻率的技術,它確保函式只在最後一次被呼叫後的一段時間後執行,通常用於處理像視窗調整大小或鍵盤輸入這樣的連續事件。
- 引數:配置物件(包含delay——延時)、需要防抖的函式。
- 返回值:返回防抖後的函式。
- 使用程式碼示例
import { debounce } from 'radash' const makeSearchRequest = (event) => { api.movies.search(event.target.value) } input.addEventListener('change', debounce({ delay: 100 }, makeSearchRequest))
- 原始碼解析
// 定義一個泛型函式 `debounce`。 export const debounce = <TArgs extends any[]>( // 第一個引數是一個物件,包含 `delay` 屬性,它是函式延遲執行的毫秒數。 { delay }: { delay: number }, // 第二個引數 `func` 是需要被防抖的函式。 func: (...args: TArgs) => any ) => { // 初始化一個變數 `timer` 用於儲存 setTimeout 的返回值。 let timer: NodeJS.Timeout | undefined = undefined // 初始化一個標誌變數 `active` 來控制函式是否應該執行。 let active = true // 定義一個防抖後的函式 `debounced`。 const debounced: DebounceFunction<TArgs> = (...args: TArgs) => { // 如果 `active` 是 `true`,則執行防抖邏輯。 if (active) { // 清除之前的定時器(如果有的話)。 clearTimeout(timer) // 設定一個新的定時器,延遲 `delay` 毫秒後執行 `func`。 timer = setTimeout(() => { // 檢查 `active` 是否仍然是 `true`,如果是,則執行 `func`。 active && func(...args) // 執行後,將 `timer` 設定為 `undefined`。 timer = undefined }, delay) } else { // 如果 `active` 是 `false`,則立即執行 `func`,不使用防抖邏輯。 func(...args) } } // 為 `debounced` 函式新增一個方法 `isPending`,用於檢查是否有等待執行的 `func`。 debounced.isPending = () => { return timer !== undefined } // 為 `debounced` 函式新增一個方法 `cancel`,用於取消執行 `func`。 debounced.cancel = () => { active = false } // 為 `debounced` 函式新增一個方法 `flush`,用於立即執行 `func`。 debounced.flush = (...args: TArgs) => func(...args) // 返回防抖後的函式 `debounced`。 return debounced }
- 方法流程說明:
- 接受一個包含延遲時間
delay
的物件和一個需要被防抖的函式func
作為引數。 - 返回一個新的函式
debounced
,這個函式在被連續呼叫時會取消之前的呼叫並重新計時。 - 如果在延遲時間
delay
內沒有再次被呼叫,func
將被執行。 debounced
函式提供了三個額外的方法:isPending
檢查是否有等待執行的函式,cancel
取消等待執行的函式,flush
立即執行函式。- 使用
debounce
函式可以幫助你控制函式的執行頻率,尤其是在處理頻繁觸發的事件時。
- 接受一個包含延遲時間
- 方法流程說明:
memo:建立一個記憶化(memoized)版本的給定函式
- 使用說明
0. 功能描述:記憶化是一種最佳化技術,它儲存函式執行的結果,並在後續呼叫中重用這個結果,以避免重複執行相同計算。這種技術特別適用於計算成本高昂或呼叫頻繁的函式。
0. 引數:需要被記憶化的函式、可選配置物件。
0. 返回值:返回一個記憶版本的函式。 - 使用程式碼示例
import { memo } from 'radash' const timestamp = memo(() => Date.now()) const now = timestamp() const later = timestamp() now === later // => true
- 原始碼解析
// 定義一個泛型函式 `memo`。 export const memo = <TArgs extends any[], TResult>( // 第一個引數 `func` 是需要被記憶化的函式。 func: (...args: TArgs) => TResult, // 第二個引數 `options` 是一個可選的配置物件。 options: { // `key` 是一個可選的函式,用於根據函式的引數生成一個唯一的快取鍵。 key?: (...args: TArgs) => string // `ttl` 是一個可選的數字,表示快取的生存時間(以毫秒為單位)。 ttl?: number } = {} // 如果沒有提供 `options`,則使用一個空物件作為預設值。 ) => { // `memo` 函式返回一個記憶化版本的 `func`。 return memoize({}, func, options.key ?? null, options.ttl ?? null) as ( // 返回的函式型別與原始 `func` 相同。 ...args: TArgs ) => TResult }
- 方法流程說明:
memo
函式接受一個函式func
和一個可選的配置物件options
。options
物件包含兩個可選的屬性:key
和ttl
。key
是一個函式,用於生成快取鍵;ttl
是快取的生存時間。memo
函式呼叫memoize
函式(在程式碼片段中未定義)來建立一個記憶化版本的func
。memoize
函式接受一個快取物件、原始函式func
、鍵生成函式options.key
和生存時間options.ttl
。- 如果沒有提供
options.key
,則使用null
作為預設值;如果沒有提供options.ttl
,也使用null
作為預設值。 memo
函式返回一個記憶化版本的func
,它的型別與原始func
相同。
- 方法流程說明:
partial :建立一個偏應用的部分函式(允許你預先填充一些引數,只需傳入剩餘引數)
- 使用說明
- 功能描述:建立一個新函式,這個新函式是原始函式
fn
的偏應用版本。偏應用(Partial Application)是一種函數語言程式設計技術,它允許你預先填充一些引數,並返回一個新函式,這個新函式只需要剩餘的引數就可以執行。 - 引數:原始函式,原始引數陣列。
- 返回值:返回一個接受剩餘的引數陣列
rest
的新函式。
- 功能描述:建立一個新函式,這個新函式是原始函式
- 使用程式碼示例
import { partial } from 'radash' const add = (a: number, b: number) => a + b const addFive = partial(add, 5) addFive(2) // => 7
- 原始碼解析
// 定義一個泛型函式 `partial`。 export const partial = <T extends any[], TA extends Partial<T>, R>( // `fn` 是原始函式,它接受一個引數陣列 `T` 並返回一個結果 `R`。 fn: (...args: T) => R, // 使用展開運算子接收一個或多個預先填充的引數陣列 `args`,其型別為 `TA`。 // `TA` 是原始引數陣列 `T` 的部分型別。 ...args: TA ) => { // `partial` 函式返回一個新函式,這個新函式接受剩餘的引數陣列 `rest`。 return (...rest: RemoveItemsInFront<T, TA>) => // 新函式呼叫原始函式 `fn`,首先展開預先填充的引數 `args`,然後展開剩餘的引數 `rest`。 // 使用型別斷言 `as T` 確保引數陣列的型別正確。 fn(...([...args, ...rest] as T)) }
- 這段程式碼中使用了幾個未定義的函式和型別,如
tryit
、list
、fork
和sort
,以及型別WorkItemResult<K>
。我們可以假設這些函式和型別具有以下功能:partial
函式接受一個原始函式fn
和一系列預先填充的引數args
。- 返回一個新函式,這個新函式接受剩餘的引數
rest
。 - 當新函式被呼叫時,它將預先填充的引數
args
和剩餘的引數rest
合併成一個完整的引數陣列,並呼叫原始函式fn
。 - 原始函式
fn
被執行,並返回結果。 - 型別
RemoveItemsInFront<T, TA>
是一個型別操作,它從型別T
中移除與TA
對應的項。它在這段程式碼中沒有定義,我們可以假設它的作用是確保rest
引數只包含原始函式fn
還需要的引數。
- 這段程式碼中使用了幾個未定義的函式和型別,如
partob:建立一個偏應用的部分函式(跟partial類似,不過接收引數不一樣)
- 使用說明
- 功能描述:建立一個新的函式,該函式是原始函式
fn
的偏應用版本。這個新函式將接受一個物件引數restobj
,它包含了原始函式fn
所需引數的剩餘部分,然後將restobj
與預先填充的引數物件argobj
合併後呼叫fn
。 - 引數:原始函式、預先填充的引數物件。
- 返回值:返回一個接收剩餘引數物件
restobj
的新函式。
- 功能描述:建立一個新的函式,該函式是原始函式
- 使用程式碼示例
import { partob } from 'radash' const add = (props: { a: number; b: number }) => props.a + props.b const addFive = partob(add, { a: 5 }) addFive({ b: 2 }) // => 7
- 原始碼解析
// 定義一個泛型函式 `partob`。 export const partob = <T, K, PartialArgs extends Partial<T>>( // `fn` 是原始函式,它接受一個型別為 `T` 的引數物件,並返回一個型別為 `K` 的結果。 fn: (args: T) => K, // `argobj` 是一個預先填充的引數物件,其型別為 `PartialArgs`,它是原始引數物件 `T` 的部分型別。 argobj: PartialArgs ) => { // `partob` 函式返回一個新的函式,該函式接受一個型別為 `Omit<T, keyof PartialArgs>` 的引數物件 `restobj`。 // `Omit<T, keyof PartialArgs>` 表示從 `T` 中省略掉 `PartialArgs` 中已有的鍵,只保留剩餘的鍵。 return (restobj: Omit<T, keyof PartialArgs>): K => // 新函式呼叫原始函式 `fn`,傳入合併後的引數物件。 fn({ // 使用展開運算子將 `argobj` 和 `restobj` 合併為一個新物件。 // 這裡的型別斷言確保合併後的物件符合原始引數物件 `T` 的型別。 ...(argobj as Partial<T>), ...(restobj as Partial<T>) } as T) }
- 方法流程說明:
partob
函式接受一個原始函式fn
和預先填充的引數物件argobj
。- 返回一個新的函式,該函式接受剩餘引數物件
restobj
。 - 當新函式被呼叫時,它將預先填充的引數物件
argobj
和剩餘引數物件restobj
合併成一個完整的引數物件,並呼叫原始函式fn
。 - 原始函式
fn
被執行,並返回結果。
- 方法流程說明:
proxied:建立動態代理物件
- 使用說明
- 功能描述:建立的代理物件可以攔截對其屬性的訪問並返回由一個處理函式
handler
產生的值。 - 引數:處理函式(該函式接受一個屬性名)。
- 返回值:返回一個新的
Proxy
物件。
- 功能描述:建立的代理物件可以攔截對其屬性的訪問並返回由一個處理函式
- 使用程式碼示例
import { proxied } from 'radash' type Property = 'name' | 'size' | 'getLocation' const person = proxied((prop: Property) => { switch (prop) { case 'name': return 'Joe' case 'size': return 20 case 'getLocation' return () => 'here' } }) person.name // => Joe person.size // => 20 person.getLocation() // => here
- 原始碼解析
// 定義一個泛型函式 `proxied`。 export const proxied = <T, K>( // `handler` 是一個函式,接受一個屬性名 `propertyName` 並返回一個型別為 `K` 的值。 handler: (propertyName: T) => K ): Record<string, K> => { // 返回一個新的 Proxy 物件。 return new Proxy( // 第一個引數是要代理的目標物件,這裡是一個空物件。 {}, // 第二個引數是一個處理器物件,它定義了多種攔截操作的方法。 { // `get` 方法用於攔截對屬性的讀取操作。 get: (target, propertyName: any) => // 當嘗試讀取屬性時,呼叫 `handler` 函式並傳入屬性名。 // 返回 `handler` 函式的結果作為屬性的值。 handler(propertyName) } ) }
- 方法流程說明:
proxied
函式接受一個handler
函式作為引數。- 使用
new Proxy()
建立一個新的Proxy
物件,它代理一個空物件。 - 定義
Proxy
物件的get
方法,用於攔截對代理物件屬性的讀取操作。 - 當嘗試讀取任何屬性時,
get
方法呼叫handler
函式,傳入被讀取的屬性名稱。 handler
函式返回的值作為屬性的值返回給呼叫者。- 返回建立的
Proxy
物件,它的型別為Record<string, K>
,表示一個物件,其屬性名為字串,屬性值的型別為K
。
- 方法流程說明:
throttle :函式節流
- 使用說明
- 功能描述:節流(Throttling)是一種控制函式呼叫頻率的技術,它確保函式在指定的時間間隔內最多隻執行一次。這通常用於限制頻繁觸發的事件(如視窗調整大小、滾動等)的處理函式。
- 引數:物件({interval})—— 觸發間隔、需要節流的函式。
- 返回值:返回節流後的函式。
- 使用程式碼示例
import { throttle } from 'radash' const onMouseMove = () => { rerender() } addEventListener('mousemove', throttle({ interval: 200 }, onMouseMove))
- 原始碼解析
// 定義一個泛型函式 `throttle`。 export const throttle = <TArgs extends any[]>( // 第一個引數是一個物件,包含 `interval` 屬性,它是函式執行之間的毫秒間隔。 { interval }: { interval: number }, // 第二個引數 `func` 是需要被節流的函式。 func: (...args: TArgs) => any ) => { // 初始化一個標誌變數 `ready`,表示函式是否準備好執行。 let ready = true // 初始化一個變數 `timer` 用於儲存 setTimeout 的返回值。 let timer: NodeJS.Timeout | undefined = undefined // 定義一個節流後的函式 `throttled`。 const throttled: ThrottledFunction<TArgs> = (...args: TArgs) => { // 如果函式尚未準備好執行,直接返回。 if (!ready) return // 呼叫 `func` 並傳入引數。 func(...args) // 設定 `ready` 為 `false`,防止函式在間隔時間內再次執行。 ready = false // 設定一個定時器,在 `interval` 毫秒後將 `ready` 重新設為 `true`,允許函式再次執行。 timer = setTimeout(() => { ready = true // 定時器執行完畢後,清除 `timer`。 timer = undefined }, interval) } // 為 `throttled` 函式新增一個方法 `isThrottled`,用於檢查函式是否處於節流狀態。 throttled.isThrottled = () => { return timer !== undefined } // 返回節流後的函式 `throttled`。 return throttled }
- 方法流程說明:
throttle
函式接受一個配置物件(包含interval
屬性)和一個需要被節流的函式func
作為引數。- 返回一個新的函式
throttled
,該函式在被連續呼叫時會限制函式的執行頻率。 - 當
throttled
函式被呼叫時,如果ready
是true
(即函式準備好執行),func
將被執行。 - 執行
func
後,ready
設定為false
,並透過setTimeout
設定一個定時器,定時器在interval
毫秒後執行,將ready
設定回true
。 - 如果
throttled
函式在定時器完成之前再次被呼叫,由於ready
是false
,func
不會被執行。 throttled
函式提供了一個額外的方法isThrottled
,用於檢查函式是否正在等待下一次執行的間隔。
- 方法流程說明:
Number相關
inRange :檢查給定數字是否在兩個數字之間
- 使用說明
- 功能描述:檢查給定數字是否在兩個數字之間。判斷包含起始值。不包含結束值。範圍的開始和結束可以是升序或降序。如果未指定結束值,則將其設定為起始值。並且把起始值設定為0。
- 引數:需要檢查的值,判斷的起始值,[判斷的結束值]。
- 返回值:在範圍內返回
true
,否則返回false
。
- 使用程式碼示例
import { inRange } from 'radash' inRange(10, 0, 20) // true inRange(9.99, 0, 10) // true inRange(Math.PI, 0, 3.15) // true inRange(10, 10, 20) // true inRange(10, 0, 10) // false inRange(1, 2) // true inRange(1, 0) // false
- 原始碼解析
// 定義一個名為 `inRange` 的函式。 function inRange(number, start, end) { // 首先檢查傳入的引數型別是否正確:`number` 和 `start` 必須是數字型別, // `end` 要麼是未定義,要麼是數字型別。 const isTypeSafe = typeof number === "number" && typeof start === "number" && (typeof end === "undefined" || typeof end === "number"); // 如果引數型別不正確,直接返回 `false`。 if (!isTypeSafe) { return false; } // 如果 `end` 引數未提供(即 `undefined`),則將 `end` 設定為 `start` 的值, // 而將 `start` 設定為 `0`。這樣就建立了一個從 `0` 到 `start` 的範圍。 if (typeof end === "undefined") { end = start; start = 0; } // 檢查 `number` 是否在 `start` 和 `end` 指定的範圍內。 // 使用 `Math.min` 和 `Math.max` 來確保 `start` 和 `end` 的順序正確, // 即使它們被反向提供(例如 `end` 小於 `start`)。 return number >= Math.min(start, end) && number < Math.max(start, end); }
- 方法流程說明:
- 首先驗證所有引數的型別。如果
number
或start
不是數字,或者end
不是數字且不是undefined
,函式返回false
。 - 如果
end
引數未定義,函式將其解釋為只提供了一個引數的情況,即一個從0
到start
的範圍。 - 函式計算
number
是否大於等於範圍的最小值(Math.min(start, end)
)並且小於範圍的最大值(Math.max(start, end)
)。 - 如果
number
在這個範圍內,函式返回true
;否則返回false
。
- 首先驗證所有引數的型別。如果
- 方法流程說明:
toFloat :可能的情況下,將一個值轉為浮點值
- 使用說明
- 功能描述:將一個值轉換為浮點數。如果轉換失敗或者提供的值是
null
或undefined
,函式將返回預設值。 - 引數:需要轉換的值、預設值。
- 返回值:能轉換則返回轉換後的浮點數,否則返回傳入的預設值。
- 功能描述:將一個值轉換為浮點數。如果轉換失敗或者提供的值是
- 使用程式碼示例
import { toFloat } from 'radash' toFloat(0) // => 0.0 toFloat(null) // => 0.0 toFloat(null, 3.33) // => 3.33
- 原始碼解析
// 定義一個名為 `toFloat` 的函式。 const toFloat = (value, defaultValue) => { // 如果沒有提供 `defaultValue`,則使用 `0` 作為預設值。 // `void 0` 是 `undefined` 的一種安全寫法。 const def = defaultValue === void 0 ? 0 : defaultValue; // 如果 `value` 是 `null` 或 `undefined`,返回預設值 `def`。 if (value === null || value === void 0) { return def; } // 嘗試將 `value` 轉換為浮點數。 const result = parseFloat(value); // 如果轉換結果是 `NaN`(Not-a-Number),返回預設值 `def`。 // 否則,返回轉換後的浮點數 `result`。 return isNaN(result) ? def : result; };
- 方法流程說明:
- 首先檢查
defaultValue
是否提供,如果沒有提供,則將def
設定為0
。 - 檢查
value
是否是null
或undefined
,如果是,返回def
。 - 使用
parseFloat
函式嘗試將value
轉換為浮點數。 - 使用
isNaN
函式檢查轉換結果是否為NaN
。 - 如果結果是
NaN
,返回def
;如果轉換成功,返回浮點數結果result
。
- 首先檢查
- 方法流程說明:
toInt:可能的情況下,將一個值轉為整數
- 使用說明
- 功能描述:將一個值轉換為整數。如果轉換失敗或者提供的值是
null
或undefined
,函式將返回預設值。 - 引數:需要轉換的值、預設值。
- 返回值:能轉換則返回轉換後的整數,否則返回傳入的預設值。
- 功能描述:將一個值轉換為整數。如果轉換失敗或者提供的值是
- 使用程式碼示例
import { toInt } from 'radash' toInt(0) // => 0 toInt(null) // => 0 toInt(null, 3) // => 3
- 原始碼解析
// 定義一個名為 `toFloat` 的函式。 const toFloat = (value, defaultValue) => { // 如果沒有提供 `defaultValue`,則使用 `0` 作為預設值。 // `void 0` 是 `undefined` 的一種安全寫法。 const def = defaultValue === void 0 ? 0 : defaultValue; // 如果 `value` 是 `null` 或 `undefined`,返回預設值 `def`。 if (value === null || value === void 0) { return def; } // 嘗試將 `value` 轉換為浮點數。 const result = parseFloat(value); // 如果轉換結果是 `NaN`(Not-a-Number),返回預設值 `def`。 // 否則,返回轉換後的浮點數 `result`。 return isNaN(result) ? def : result; };
- 方法流程說明:
- 首先檢查
defaultValue
是否提供,如果沒有提供,則將def
設定為0
。 - 檢查
value
是否是null
或undefined
,如果是,返回def
。 - 使用
parseFloat
函式嘗試將value
轉換為浮點數。 - 使用
isNaN
函式檢查轉換結果是否為NaN
。 - 如果結果是
NaN
,返回def
;如果轉換成功,返回浮點數結果result
。
- 首先檢查
- 方法流程說明:
寫在後面
- 老實說能看到這裡的人呢,都是美麗帥氣有眼光又多金的人才,感謝閱讀到最後,Peace and Love!
- 後續我們會繼續分享
Radash
庫中其他方法的使用和原始碼解析。 - 大家有任何問題或見解,歡迎評論區留言交流和批評指正!!!
- 你的每一個點贊和收藏都是作者寫作的動力!!!(求個點贊《《小聲逼逼》》)
- 點選訪問:radash官網