函数工具 (function.ts)
函数工具类,提供复制文本、HTTP配置、防抖节流、函数执行控制、函数转换组合、异步函数工具等实用功能。
📖 概述
函数工具库包含以下功能类别:
- 复制文本到剪贴板:复制文本并显示提示
- 快速创建带headers的配置:HTTP请求配置工具
- 防抖与节流:控制函数触发频率
- 函数执行控制:控制函数执行方式
- 函数转换与组合:转换函数调用方式
- 异步函数工具:处理异步函数执行
📋 复制和HTTP工具
copy
复制文本到剪贴板并显示提示。
typescript
copy(text: string, message?: string): Promise<boolean>
参数:
text
- 要复制的文本message
- 可选的提示消息,不传则使用默认提示
返回值:
Promise<boolean>
- 是否复制成功
特性:
- 优先使用现代 Clipboard API
- 自动降级到传统
execCommand
方法 - 智能提示消息(长文本显示"复制成功",短文本显示具体内容)
- 自动显示成功/失败提示
示例:
typescript
// 基本用法
await copy('Hello World') // 显示:"复制成功: Hello World"
// 自定义提示消息
await copy('很长的文本内容...', '代码复制成功') // 显示:"代码复制成功"
// 检查复制结果
const success = await copy('text to copy')
if (success) {
console.log('复制成功')
} else {
console.log('复制失败')
}
// 复制长文本
const longText = '这是一段很长的文本...' // 超过200字符
await copy(longText) // 显示:"复制成功"(不显示具体内容)
withHeaders
快速创建带headers的HTTP配置。
typescript
withHeaders(headers: CustomHeaders, config?: AxiosRequestConfig): AxiosRequestConfig
参数:
headers
- 自定义请求头config
- 可选的Axios配置对象
返回值:
AxiosRequestConfig
- 包含headers的配置对象
示例:
typescript
// 添加认证头
const config = withHeaders({
'Authorization': 'Bearer token123',
'Content-Type': 'application/json'
})
// 合并现有配置
const config2 = withHeaders(
{ 'X-API-Key': 'key123' },
{ timeout: 5000, method: 'POST' }
)
// 在API调用中使用
axios.get('/api/data', withHeaders({ 'Authorization': `Bearer ${token}` }))
⏱️ 防抖与节流
debounce
函数防抖,在指定时间内多次调用,只执行最后一次(或第一次)。
typescript
debounce<T extends (...args: any[]) => any>(
func: T,
wait: number = 300,
immediate: boolean = false
): ((...args: Parameters<T>) => void)
参数:
func
- 要防抖的函数wait
- 等待时间(毫秒),默认300msimmediate
- 是否立即执行,默认false
返回值:
Function
- 防抖处理后的函数,包含cancel
方法用于取消
使用场景:
- 搜索框输入防抖
- 窗口resize事件
- 按钮点击防重复
- 表单提交防抖
示例:
typescript
// 搜索框防抖
const searchDebounced = debounce((keyword: string) => {
console.log('搜索:', keyword)
}, 500)
// 用户输入时调用
input.addEventListener('input', (e) => {
searchDebounced(e.target.value)
})
// 窗口调整大小防抖
const handleResize = debounce(() => {
console.log('窗口大小改变')
}, 200)
window.addEventListener('resize', handleResize)
// 立即执行模式(第一次点击立即响应,后续300ms内忽略)
const handleClick = debounce(() => {
console.log('按钮被点击')
}, 300, true)
button.addEventListener('click', handleClick)
// 取消防抖
const debouncedFn = debounce(someFunction, 1000)
debouncedFn.cancel() // 取消待执行的函数
throttle
函数节流,在指定时间内,函数最多执行一次。
typescript
throttle<T extends (...args: any[]) => any>(
func: T,
wait: number = 300,
options: { leading?: boolean; trailing?: boolean } = {}
): ((...args: Parameters<T>) => ReturnType<T>)
参数:
func
- 要节流的函数wait
- 等待时间(毫秒),默认300msoptions
- 配置选项leading
- 是否在开始时执行一次,默认truetrailing
- 是否在结束时再执行一次,默认true
返回值:
Function
- 节流处理后的函数,包含cancel
方法
使用场景:
- 滚动事件优化
- 鼠标移动事件
- API请求限频
- 按钮连击限制
示例:
typescript
// 滚动事件节流
const handleScroll = throttle(() => {
console.log('滚动位置:', window.scrollY)
}, 200)
window.addEventListener('scroll', handleScroll)
// API请求节流
const saveData = throttle(async (data) => {
await api.saveData(data)
}, 1000)
// 不在开始和结束时执行
const throttledFn = throttle(someFunction, 200, {
leading: false,
trailing: false
})
// 取消节流
const throttledFn2 = throttle(someFunction, 1000)
throttledFn2.cancel() // 取消定时器
🎮 函数执行控制
once
确保函数只执行一次。
typescript
once<T extends (...args: any[]) => any>(func: T): ((...args: Parameters<T>) => ReturnType<T>)
参数:
func
- 要控制的函数
返回值:
Function
- 包装后的函数,只会执行一次
使用场景:
- 初始化函数
- 事件监听器
- 资源加载
- 单例模式
示例:
typescript
// 初始化操作,只执行一次
const initialize = once(() => {
console.log('系统初始化...')
// 初始化代码
})
// 多次调用,实际只执行一次
initialize() // 输出:"系统初始化..."
initialize() // 不执行
initialize() // 不执行
// 带返回值的函数
const getConfig = once(() => {
console.log('加载配置...')
return { theme: 'dark', lang: 'zh-CN' }
})
const config1 = getConfig() // 执行并返回配置
const config2 = getConfig() // 直接返回之前的结果
console.log(config1 === config2) // true
delay
延迟执行函数。
typescript
delay<T extends (...args: any[]) => any>(
func: T,
wait: number = 0,
...args: Parameters<T>
): Promise<ReturnType<T>>
参数:
func
- 要延迟执行的函数wait
- 延迟时间(毫秒),默认0...args
- 传递给函数的参数
返回值:
Promise<ReturnType<T>>
- Promise,函数执行后resolve
示例:
typescript
// 延迟1秒后执行
await delay(() => {
console.log('延迟执行')
}, 1000)
// 延迟执行带参数的函数
await delay(console.log, 500, 'Hello', 'World')
// 延迟处理用户操作
button.addEventListener('click', async () => {
showLoading()
await delay(processData, 300, userData)
hideLoading()
})
// 模拟网络延迟
const mockApiCall = async (data) => {
await delay(() => {}, 1000) // 模拟1秒延迟
return { success: true, data }
}
retry
尝试多次执行函数,直到成功或达到最大尝试次数。
typescript
retry<T>(
func: () => Promise<T> | T,
options: {
maxAttempts?: number
delay?: number
backoff?: number
} = {}
): Promise<T>
参数:
func
- 要执行的函数,应返回promise或值options
- 配置选项maxAttempts
- 最大尝试次数,默认3delay
- 尝试间隔(毫秒),默认1000backoff
- 间隔增长系数,默认2(指数退避)
返回值:
Promise<T>
- 成功执行后resolve,或者尝试次数用完后reject
使用场景:
- 网络请求重试
- 文件操作重试
- 数据库连接重试
- 外部服务调用
示例:
typescript
// 网络请求重试
const fetchData = async () => {
const response = await fetch('/api/data')
if (!response.ok) {
throw new Error('请求失败')
}
return response.json()
}
try {
const data = await retry(fetchData, {
maxAttempts: 5,
delay: 2000,
backoff: 2
})
console.log('数据获取成功:', data)
} catch (error) {
console.error('重试5次后仍然失败:', error.message)
}
// 文件读取重试
const readFile = async () => {
// 可能失败的文件读取操作
return fs.readFile('config.json', 'utf8')
}
const config = await retry(readFile, { maxAttempts: 3, delay: 500 })
withTimeout
为函数添加超时控制。
typescript
withTimeout<T, A extends any[]>(
func: (...args: A) => Promise<T>,
ms: number
): ((...args: A) => Promise<T>)
参数:
func
- 异步函数ms
- 超时时间(毫秒)
返回值:
Function
- 带超时控制的函数
示例:
typescript
// 为API请求添加5秒超时
const fetchWithTimeout = withTimeout(fetch, 5000)
try {
const response = await fetchWithTimeout('/api/slow-endpoint')
} catch (error) {
if (error.name === 'TimeoutError') {
console.log('请求超时')
}
}
// 为数据处理添加超时
const processDataWithTimeout = withTimeout(processLargeData, 10000)
try {
const result = await processDataWithTimeout(largeDataset)
} catch (error) {
console.log('数据处理超时或出错:', error.message)
}
🔧 函数转换与组合
curry
柯里化函数,将接受多个参数的函数转换为一系列接受单个参数的函数。
typescript
curry<T extends (...args: any[]) => any>(func: T): any
参数:
func
- 要柯里化的函数
返回值:
Function
- 柯里化后的函数
使用场景:
- 函数复用
- 参数预设
- 函数组合
- 配置工厂
示例:
typescript
// 基本柯里化
const add = (a, b, c) => a + b + c
const curriedAdd = curry(add)
// 多种调用方式
curriedAdd(1)(2)(3) // 6
curriedAdd(1, 2)(3) // 6
curriedAdd(1)(2, 3) // 6
curriedAdd(1, 2, 3) // 6
// 创建专用函数
const add10 = curriedAdd(10)
const add10And5 = add10(5)
console.log(add10And5(3)) // 18
// 实际应用:格式化函数
const formatMessage = curry((level, module, message) => {
return `[${level}] ${module}: ${message}`
})
const logError = formatMessage('ERROR')
const logUserError = logError('USER')
console.log(logUserError('登录失败')) // "[ERROR] USER: 登录失败"
partial
偏函数应用,固定函数的部分参数。
typescript
partial<T extends (...args: any[]) => any>(
func: T,
...partialArgs: any[]
): ((...args: any[]) => ReturnType<T>)
参数:
func
- 原始函数...partialArgs
- 要固定的参数
返回值:
Function
- 新函数,接受剩余参数
示例:
typescript
// 基本用法
const multiply = (a, b) => a * b
const double = partial(multiply, 2)
console.log(double(4)) // 8
// API调用封装
const fetchFromApi = (endpoint, params, options) => {
return fetch(`/api/${endpoint}`, { ...options, params })
}
// 创建专用的API调用函数
const fetchUsers = partial(fetchFromApi, 'users')
const fetchUsersPaginated = partial(fetchUsers, { page: 1, size: 10 })
// 使用
fetchUsersPaginated({ sort: 'name' })
// 等同于 fetchFromApi('users', { page: 1, size: 10 }, { sort: 'name' })
// 事件处理
const handleEvent = (eventType, element, callback, event) => {
console.log(`${eventType} on ${element.tagName}`)
callback(event)
}
const handleClick = partial(handleEvent, 'click')
const handleButtonClick = partial(handleClick, document.getElementById('btn'))
// 绑定事件
button.addEventListener('click', handleButtonClick(e => console.log('clicked')))
memoize
记忆化函数,缓存函数的计算结果。
typescript
memoize<T extends (...args: any[]) => any>(
func: T,
resolver?: (...args: Parameters<T>) => string
): ((...args: Parameters<T>) => ReturnType<T>)
参数:
func
- 要记忆化的函数resolver
- 可选的键解析器函数,用于生成缓存键
返回值:
Function
- 记忆化后的函数
使用场景:
- 递归算法优化
- 昂贵的计算缓存
- API请求缓存
- 纯函数优化
示例:
typescript
// 斐波那契数列优化
const fibonacci = memoize((n) => {
if (n <= 1) return n
return fibonacci(n - 1) + fibonacci(n - 2)
})
console.time('fib40')
console.log(fibonacci(40)) // 快速计算,不会重复计算子问题
console.timeEnd('fib40')
// API请求缓存
const fetchUser = memoize(async (userId) => {
console.log(`正在获取用户 ${userId} 的信息`)
const response = await fetch(`/api/users/${userId}`)
return response.json()
})
// 多次调用相同参数,只会发送一次请求
const user1 = await fetchUser('123') // 发送请求
const user2 = await fetchUser('123') // 使用缓存
console.log(user1 === user2) // true
// 自定义键解析器
const getUser = memoize(
async (id, options) => {
return api.fetchUser(id, options)
},
(id, options) => `user:${id}:${JSON.stringify(options)}`
)
// 计算密集型函数
const expensiveCalculation = memoize((data) => {
console.log('执行复杂计算...')
// 复杂的计算逻辑
return data.reduce((sum, item) => sum + Math.sqrt(item), 0)
})
⚡ 异步函数工具
serial
串行执行异步函数。
typescript
serial<T>(funcs: ((arg?: any) => Promise<any>)[], initial?: any): Promise<T>
参数:
funcs
- 异步函数数组initial
- 初始值
返回值:
Promise<T>
- 最终结果
示例:
typescript
// 数据处理流水线
const processData = await serial([
() => fetchRawData(),
(data) => validateData(data),
(validData) => transformData(validData),
(transformedData) => saveData(transformedData)
])
// 初始化步骤
await serial([
() => connectDatabase(),
() => loadConfiguration(),
() => startServices(),
() => scheduleJobs()
])
parallel
并行执行异步函数并限制并发数。
typescript
parallel<T>(tasks: (() => Promise<T>)[], concurrency: number = Infinity): Promise<T[]>
参数:
tasks
- 异步任务数组concurrency
- 并发限制数,默认无限制
返回值:
Promise<T[]>
- 所有任务的结果数组
示例:
typescript
// 并行处理文件,最多同时处理3个
const files = ['file1.txt', 'file2.txt', 'file3.txt', 'file4.txt', 'file5.txt']
const tasks = files.map(filename => () => processFile(filename))
const results = await parallel(tasks, 3)
console.log('所有文件处理完成:', results)
// 批量API请求
const userIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const fetchTasks = userIds.map(id => () => fetchUser(id))
// 限制并发为5,避免服务器压力过大
const users = await parallel(fetchTasks, 5)
withRetry
为异步函数添加错误重试功能。
typescript
withRetry<T extends (...args: any[]) => Promise<any>>(
asyncFn: T,
options: {
retries?: number
retryDelay?: number
shouldRetry?: (error: any) => boolean
} = {}
): ((...args: Parameters<T>) => Promise<ReturnType<T>>)
参数:
asyncFn
- 异步函数options
- 重试选项retries
- 重试次数,默认3retryDelay
- 重试延迟,默认300msshouldRetry
- 是否应该重试的判断函数
返回值:
Function
- 增强后的异步函数
示例:
typescript
// 网络请求重试
const fetchWithRetry = withRetry(fetchData, {
retries: 3,
retryDelay: 1000,
shouldRetry: (err) => err.status === 500 || err.status === 502
})
// 只在特定错误时重试
const apiCall = withRetry(callExternalAPI, {
retries: 5,
retryDelay: 2000,
shouldRetry: (error) => {
// 只在网络错误或服务器错误时重试
return error.code === 'NETWORK_ERROR' || error.status >= 500
}
})
const result = await apiCall(requestData)
rateLimit
限制函数执行频率。
typescript
rateLimit<T extends (...args: any[]) => any>(
fn: T,
limit: number,
interval: number
): ((...args: Parameters<T>) => Promise<ReturnType<T>>)
参数:
fn
- 要限制的函数limit
- 限制次数interval
- 时间间隔(毫秒)
返回值:
Function
- 限制后的函数
示例:
typescript
// 限制API调用每分钟最多100次
const limitedFetch = rateLimit(fetch, 100, 60000)
// 限制发送邮件每小时最多10次
const sendEmailLimited = rateLimit(sendEmail, 10, 3600000)
// 使用
for (let i = 0; i < 200; i++) {
// 自动排队,遵守频率限制
limitedFetch(`/api/data/${i}`)
}
💡 使用技巧
1. 搜索框优化
结合防抖和节流优化用户体验:
typescript
// 搜索防抖 + 请求节流
const searchInput = document.getElementById('search')
const searchResults = document.getElementById('results')
const performSearch = throttle(async (keyword) => {
if (!keyword.trim()) return
const results = await fetchSearchResults(keyword)
renderResults(results)
}, 500)
const debouncedSearch = debounce((keyword) => {
performSearch(keyword)
}, 300)
searchInput.addEventListener('input', (e) => {
debouncedSearch(e.target.value)
})
2. 配置化API调用
使用柯里化和偏函数创建配置化的API调用:
typescript
// 基础API函数
const apiCall = curry((method, endpoint, headers, data) => {
return fetch(`/api/${endpoint}`, {
method,
headers,
body: JSON.stringify(data)
})
})
// 创建专用函数
const get = apiCall('GET')
const post = apiCall('POST')
const authGet = get(null, { 'Authorization': `Bearer ${token}` })
// 使用
const userData = await authGet('user/profile')
const createUser = await post('users', { 'Content-Type': 'application/json' })
3. 错误处理和重试策略
构建健壮的异步操作:
typescript
// 带重试和超时的网络请求
const robustFetch = withRetry(
withTimeout(fetch, 10000), // 10秒超时
{
retries: 3,
retryDelay: 1000,
shouldRetry: (error) => {
return error.name === 'TimeoutError' ||
error.status >= 500 ||
error.code === 'NETWORK_ERROR'
}
}
)
// 使用
try {
const data = await robustFetch('/api/important-data')
} catch (error) {
console.error('经过重试后仍然失败:', error)
}
4. 批处理优化
处理大量数据时的性能优化:
typescript
// 大量数据的批处理
const processLargeDataset = async (items) => {
// 将数据分组,每组100个
const chunks = []
for (let i = 0; i < items.length; i += 100) {
chunks.push(items.slice(i, i + 100))
}
// 并行处理,限制并发为5
const processTasks = chunks.map(chunk => () => processChunk(chunk))
const results = await parallel(processTasks, 5)
return results.flat()
}
// 记忆化昂贵的计算
const expensiveCalc = memoize((data) => {
// 复杂计算
return data.reduce((acc, item) => {
// 昂贵的操作
return acc + heavyCalculation(item)
}, 0)
})
⚠️ 注意事项
- 内存泄漏:记忆化函数会保存缓存,长期运行的应用需要注意内存使用
- 异步异常:在异步函数工具中,确保正确处理Promise的reject
- 上下文绑定:使用防抖、节流时注意函数的this绑定问题
- 取消机制:及时取消不需要的防抖、节流函数,避免内存泄漏
- 错误传播:在函数组合中确保错误能正确向上传播