Skip to content

memo

創建一個記憶化函數,緩存函數結果以避免重復計算。

基礎用法

typescript
import { memo } from 'radash'

const expensiveCalculation = memo((n: number) => {
  console.log('Computing...')
  return n * n * n
})

console.log(expensiveCalculation(5)) // Computing... 125
console.log(expensiveCalculation(5)) // 125 (從緩存返回)
console.log(expensiveCalculation(3)) // Computing... 27
console.log(expensiveCalculation(5)) // 125 (從緩存返回)

語法

typescript
function memo<T extends (...args: any[]) => any>(
  fn: T,
  options?: {
    maxSize?: number
    ttl?: number
  }
): T

參數

  • fn (function): 要記憶化的函數
  • options (object, 可選): 配置選項
    • maxSize (number, 可選): 緩存的最大條目數
    • ttl (number, 可選): 緩存條目的生存時間(毫秒)

返回值

返回一個記憶化後的函數,具有相同的簽名但會緩存結果。

示例

基本記憶化

typescript
import { memo } from 'radash'

const fibonacci = memo((n: number): number => {
  console.log(`Computing fibonacci(${n})`)
  if (n <= 1) return n
  return fibonacci(n - 1) + fibonacci(n - 2)
})

console.log(fibonacci(10)) // 計算並緩存
console.log(fibonacci(10)) // 從緩存返回
console.log(fibonacci(5))  // 從緩存返回

復雜參數記憶化

typescript
import { memo } from 'radash'

interface User {
  id: number
  name: string
  age: number
}

const getUserStats = memo((users: User[], filter: string) => {
  console.log('Computing user stats...')
  
  const filtered = users.filter(user => 
    user.name.toLowerCase().includes(filter.toLowerCase())
  )
  
  return {
    count: filtered.length,
    averageAge: filtered.reduce((sum, user) => sum + user.age, 0) / filtered.length,
    names: filtered.map(user => user.name)
  }
})

const users: User[] = [
  { id: 1, name: 'Alice', age: 25 },
  { id: 2, name: 'Bob', age: 30 },
  { id: 3, name: 'Charlie', age: 35 }
]

console.log(getUserStats(users, 'alice')) // 計算
console.log(getUserStats(users, 'alice')) // 從緩存返回
console.log(getUserStats(users, 'bob'))   // 計算

異步函數記憶化

typescript
import { memo } from 'radash'

const fetchUserData = memo(async (userId: number) => {
  console.log(`Fetching user ${userId}...`)
  const response = await fetch(`/api/users/${userId}`)
  return response.json()
})

// 使用記憶化的異步函數
async function getUser(userId: number) {
  const user = await fetchUserData(userId)
  console.log('User:', user)
  return user
}

// 第一次調用會發起請求
await getUser(1)
// 第二次調用會從緩存返回
await getUser(1)

帶緩存大小限制

typescript
import { memo } from 'radash'

const expensiveFunction = memo((input: string) => {
  console.log(`Computing for: ${input}`)
  return input.split('').reverse().join('')
}, { maxSize: 3 })

console.log(expensiveFunction('hello')) // 計算
console.log(expensiveFunction('world')) // 計算
console.log(expensiveFunction('test'))  // 計算
console.log(expensiveFunction('hello')) // 從緩存返回
console.log(expensiveFunction('new'))   // 計算,會清除最舊的緩存

帶生存時間的緩存

typescript
import { memo } from 'radash'

const getCurrentTime = memo(() => {
  console.log('Getting current time...')
  return new Date().toISOString()
}, { ttl: 5000 }) // 5秒緩存

console.log(getCurrentTime()) // 計算
console.log(getCurrentTime()) // 從緩存返回

// 5秒後
setTimeout(() => {
  console.log(getCurrentTime()) // 重新計算
}, 6000)

對象參數記憶化

typescript
import { memo } from 'radash'

const processConfig = memo((config: Record<string, any>) => {
  console.log('Processing config...')
  return {
    ...config,
    processed: true,
    timestamp: Date.now()
  }
})

const config1 = { theme: 'dark', language: 'en' }
const config2 = { theme: 'light', language: 'zh' }

console.log(processConfig(config1)) // 計算
console.log(processConfig(config1)) // 從緩存返回
console.log(processConfig(config2)) // 計算

數組參數記憶化

typescript
import { memo } from 'radash'

const calculateArrayStats = memo((numbers: number[]) => {
  console.log('Calculating array stats...')
  return {
    sum: numbers.reduce((acc, num) => acc + num, 0),
    average: numbers.reduce((acc, num) => acc + num, 0) / numbers.length,
    min: Math.min(...numbers),
    max: Math.max(...numbers)
  }
})

const numbers1 = [1, 2, 3, 4, 5]
const numbers2 = [6, 7, 8, 9, 10]

console.log(calculateArrayStats(numbers1)) // 計算
console.log(calculateArrayStats(numbers1)) // 從緩存返回
console.log(calculateArrayStats(numbers2)) // 計算

多參數記憶化

typescript
import { memo } from 'radash'

const complexCalculation = memo((a: number, b: number, operation: string) => {
  console.log(`Computing ${a} ${operation} ${b}...`)
  
  switch (operation) {
    case 'add':
      return a + b
    case 'multiply':
      return a * b
    case 'divide':
      return a / b
    default:
      return a - b
  }
})

console.log(complexCalculation(5, 3, 'add'))      // 計算
console.log(complexCalculation(5, 3, 'add'))      // 從緩存返回
console.log(complexCalculation(5, 3, 'multiply')) // 計算

遞歸函數記憶化

typescript
import { memo } from 'radash'

const factorial = memo((n: number): number => {
  console.log(`Computing factorial(${n})`)
  if (n <= 1) return 1
  return n * factorial(n - 1)
})

console.log(factorial(5)) // 計算
console.log(factorial(5)) // 從緩存返回
console.log(factorial(3)) // 從緩存返回(因為factorial(3)在計算factorial(5)時已經計算過)

條件記憶化

typescript
import { memo } from 'radash'

const conditionalMemo = memo((value: string, shouldCache: boolean) => {
  console.log(`Processing: ${value}`)
  return value.toUpperCase()
})

// 根據條件決定是否使用緩存
console.log(conditionalMemo('hello', true))  // 計算並緩存
console.log(conditionalMemo('hello', true))  // 從緩存返回
console.log(conditionalMemo('hello', false)) // 計算(不同的參數)

緩存清理

typescript
import { memo } from 'radash'

const cachedFunction = memo((input: string) => {
  console.log(`Processing: ${input}`)
  return input.length
}, { maxSize: 2 })

console.log(cachedFunction('hello')) // 計算
console.log(cachedFunction('world')) // 計算
console.log(cachedFunction('test'))  // 計算,會清除'hello'的緩存
console.log(cachedFunction('hello')) // 重新計算

性能優化示例

typescript
import { memo } from 'radash'

// 模擬昂貴的計算
const expensiveOperation = memo((data: number[]) => {
  console.log('Performing expensive operation...')
  
  // 模擬耗時計算
  let result = 0
  for (let i = 0; i < 1000000; i++) {
    result += data.reduce((sum, num) => sum + num, 0)
  }
  
  return result
})

const data1 = [1, 2, 3, 4, 5]
const data2 = [6, 7, 8, 9, 10]

console.time('First call')
console.log(expensiveOperation(data1))
console.timeEnd('First call')

console.time('Cached call')
console.log(expensiveOperation(data1))
console.timeEnd('Cached call')

console.time('New data call')
console.log(expensiveOperation(data2))
console.timeEnd('New data call')

錯誤處理

typescript
import { memo } from 'radash'

const riskyFunction = memo((input: string) => {
  console.log(`Processing: ${input}`)
  
  if (input === 'error') {
    throw new Error('Simulated error')
  }
  
  return input.toUpperCase()
})

try {
  console.log(riskyFunction('hello')) // 正常工作
  console.log(riskyFunction('hello')) // 從緩存返回
  console.log(riskyFunction('error')) // 拋出錯誤
} catch (error) {
  console.error('Error:', error.message)
}

注意事項

  1. 內存使用: 緩存會佔用內存,注意設置合適的maxSize
  2. 參數比較: 使用嚴格相等比較參數
  3. 異步函數: 支持異步函數,但緩存的是Promise
  4. 副作用: 避免在記憶化函數中使用副作用
  5. 緩存失效: 使用ttl可以設置緩存過期時間

與其他方法的區別

  • memo(): radash提供的記憶化工具
  • 手動緩存: 需要自己實現緩存邏輯
  • useMemo(): React Hook,僅用於組件內

實際應用場景

  1. 數學計算: 緩存昂貴的數學運算
  2. API調用: 緩存API響應
  3. 數據處理: 緩存數據處理結果
  4. 遞歸函數: 優化遞歸算法性能
  5. 配置處理: 緩存配置計算結果

Released under the MIT License.