工具函数✅
本主题汇总日常开发中常见的工具函数实现。
实现 lodash debounce ?
/**
* 实现 loadash 的 debounce 函数
* @param {Function} func 要 doubunce 的函数
* @param {number} wait 延迟时间,单位毫秒
* @param {Object} options 可选参数
* @param {boolean} options.leading 是否在延迟开始前调用函数
* @param {boolean} options.trailing 是否在延迟结束后调用函数
* @param {boolean} options.maxWait 最大等待时间,单位毫秒, 避免函数被频繁调用导致一直无法触发,设置一个最大等待时间,确保函数执行
* @returns {Function} 返回一个新的防抖函数
* @example
*
* const debouncedFunc = debounce(() => {
* console.log('Function executed');
* }, 1000, { leading: true, trailing: false });
* debouncedFunc(); // 立即执行
* setTimeout(debouncedFunc, 500); // 不会执行
* setTimeout(debouncedFunc, 1000); // 会执行
*/
module.exports = function debounce (func, wait = 0, options = {}) {
}
答案
debounce 用于限制函数的执行频率,避免在短时间内多次触发同一事件。参考 lodash debounce 函数,实现如下:
提示
这个概念实际上来源单片机开发中常见的按键防抖,因为在按下物理按键时,可能由于键盘内部弹簧的机械抖动,导致按键状态在短时间内多次变化,这会导致程序误判为多次按键事件。所以单片机中会通过延时来忽略短时间内的多次按键事件。
答案中包含了 lodash 的全量用例,实现中注意如下细节
- lodash 的 debounce 会返回前一次执行的结果
- lodash 的返回函数支持 cancel, 和 flush 函数
- cancel 用于取消防抖函数的执行
- flush 用于立即执行防抖函数
- maxWait 参数用于设置最大等待时间,注意 maxWait 的执行是同步
- this 延迟执行后的指向问题
实现 lodash throttle
/**
* 实现 loadash 的 throttle 函数
* @param {Function} func 要 throttle 的函数
* @param {number} wait 等待时间,单位毫秒
* @param {Object} options 可选参数
* @param {boolean} options.leading 是否在开始时调用函数
* @param {boolean} options.trailing 是否在结束时调用函数
* @returns {Function} 返回一个新的节流函数
* @example
*
*/
module.exports = function throttle (func, wait = 0, options = {}) {
}
答案
throttle 用于限制函数的执行频率,对高频执行的函数稳定频率执行。参考 lodash throttle 函数,实现如下:
提示
在 lodash 内部,实际上 throttle 是基于 debounce 实现的,主要区别在于 throttle 默认配置会在开始和结束时调用函数,同时 maxWait 参数的值会被设置为 wait 的值。
实现 loadash get
检测对象循环引用
实现字符串过长显示省略号?
实现并发异步调度器
保证同时运行的任务限制。完善代码中 Scheduler 类,使得以下程序能正确输出:
// 实现带并发限制的异步调度器
class Scheduler {
// Your code
}
// 异步任务函数
const fetchUser = (name, delay) => {
return () => new Promise((resolve) => {
setTimeout(() => {
() => console.log(name)
resolve()
}, delay)
})
}
const scheduler = new Scheduler(2) // 控制并发数 2
scheduler.add(fetchUser('A', 2000))
scheduler.add(fetchUser('B', 1000))
scheduler.add(fetchUser('C', 800))
scheduler.add(fetchUser('D', 500))
// 打印顺序: B C A D
class Scheduler {
constructor (concurrency) {
this.concurrency = concurrency
this.tasks = []
this.running = 0
}
add (task) {
return new Promise((resolve) => {
this.tasks.push({
task,
resolve
})
this.schedule()
})
}
schedule () {
while (this.tasks.length > 0 && this.running < this.concurrency) {
const current = this.tasks.shift()
this.running++
current.task().then((result) => {
this.running--
current.resolve(result)
this.schedule()
})
}
}
}
实现 dayjs format 函数
答案
// dayjs format 函数实现
function format (format) {
const date = this
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hours = date.getHours()
const minutes = date.getMinutes()
const seconds = date.getSeconds()
format = format.replace('YYYY', year)
format = format.replace('MM', month.toString().padStart(2, '0'))
format = format.replace('DD', day.toString().padStart(2, '0'))
format = format.replace('HH', hours.toString().padStart(2, '0'))
format = format.replace('hh', (hours % 12).toString().padStart(2, '0'))
format = format.replace('mm', minutes.toString().padStart(2, '0'))
format = format.replace('ss', seconds.toString().padStart(2, '0'))
return format
}
// 示例用法
const date = new Date()
const formattedDate = date.format('YYYY-MM-DD HH:mm:ss')
console.log(formattedDate) // 输出结果为当前日期和时间的格式化字符串
实现类似 dayjs difference 函数
答案
// dayjs difference 函数实现
function difference (date1, date2, unit = 'day') {
const diffInMs = date2 - date1
switch (unit) {
case 'year':
return diffInMs / (1000 * 60 * 60 * 24 * 365)
case 'month':
return diffInMs / (1000 * 60 * 60 * 24 * 30)
case 'day':
return diffInMs / (1000 * 60 * 60 * 24)
case 'hour':
return diffInMs / (1000 * 60 * 60)
case 'minute':
return diffInMs / (1000 * 60)
case 'second':
return diffInMs / 1000
default:
throw new Error('Unsupported unit for difference calculation')
}
}
// 示例用法
const date1 = new Date('2023-01-01')
const date2 = new Date('2024-01-01')
const diffInDays = difference(date1, date2, 'day')
console.log(`Difference in days: ${diffInDays}`) // 输出结果为 365
实现 lodash isEqual 函数
实现管道函数
答案
实现一个缓存函数
如何做 promise 缓存?上一次调用函数的 promise 没有返回, 那么下一次调用函数依然返回上一个 promise
答案
function cachedPromise (promiseFunction) {
let lastPromise = null
return function () {
// 如果有未完成的 Promise,直接返回
if (lastPromise) return lastPromise
// 创建新的 Promise,并在完成后重置缓存
lastPromise = promiseFunction().finally(() => {
lastPromise = null
})
return lastPromise
}
}
// 示例异步函数
const promiseFunction = () =>
new Promise(resolve => setTimeout(() => resolve('Resolved!'), 2000))
const cachedPromiseFunction = cachedPromise(promiseFunction)
// 多次调用,未完成时返回同一个 Promise
cachedPromiseFunction().then(console.log) // Resolved!
cachedPromiseFunction().then(console.log) // Resolved!
setTimeout(() => {
// 上一次已完成,会返回新的 Promise
cachedPromiseFunction().then(console.log) // Resolved!
}, 3000)
可以用闭包实现 Promise 缓存,让多次调用返回同一个未完成的 Promise。核心思路是用一个变量保存上一次的 Promise,只有当它完成后才会生成新的 Promise。这样可以避免重复请求或重复执行异步操作。
下面是标准实现方式:
function cachedPromise (promiseFunction) {
let lastPromise = null
return function () {
// 如果有未完成的 Promise,直接返回
if (lastPromise) return lastPromise
// 创建新的 Promise,并在完成后重置缓存
lastPromise = promiseFunction().finally(() => {
lastPromise = null
})
return lastPromise
}
}
// 示例异步函数
const promiseFunction = () =>
new Promise(resolve => setTimeout(() => resolve('Resolved!'), 2000))
const cachedPromiseFunction = cachedPromise(promiseFunction)
// 多次调用,未完成时返回同一个 Promise
cachedPromiseFunction().then(console.log) // Resolved!
cachedPromiseFunction().then(console.log) // Resolved!
setTimeout(() => {
// 上一次已完成,会返回新的 Promise
cachedPromiseFunction().then(console.log) // Resolved!
}, 3000)
要点说明:
- 用
lastPromise
缓存上一次的 Promise。 - 用
finally
保证 Promise 完成后重置缓存。 - 这样只有上一次 Promise 未完成时才会复用,否则会重新生成。
常见坑:
- 不能直接判断 Promise 状态,需用
finally
或第三方库(如 Bluebird)。 - 适用于防止重复请求、节流等场景。
如需兼容更复杂的状态检测,可考虑引入第三方 Promise 库。
数字千分化的实现方式有哪些?用代码实现一下
答案
方式 | 说明 | 示例代码 |
---|---|---|
toLocaleString | 使用内置方法,简单可靠 | 1234567..toLocaleString() |
正则替换 | 用正则表达式插入逗号 | '1234567'.replace(/\B(?=(\d{3})+(?!\d))/g, ',') |