以下是手写的防抖、节流和函数柯里化实现:
1. 防抖 (Debounce)
function debounce(fn, delay, immediate = false) {
let timer = null;
let isInvoked = false;
return function(...args) {
const context = this;
// 清除之前的定时器
if (timer) clearTimeout(timer);
// 立即执行
if (immediate && !isInvoked) {
fn.apply(context, args);
isInvoked = true;
} else {
timer = setTimeout(() => {
fn.apply(context, args);
isInvoked = false;
}, delay);
}
};
}
// 使用示例
const debouncedFn = debounce(() => {
console.log('执行了');
}, 1000);
2. 节流 (Throttle)
时间戳版本(立即执行)
function throttle(fn, delay) {
let lastTime = 0;
return function(...args) {
const context = this;
const now = Date.now();
if (now - lastTime >= delay) {
fn.apply(context, args);
lastTime = now;
}
};
}
定时器版本(延迟执行)
function throttle(fn, delay) {
let timer = null;
return function(...args) {
const context = this;
if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args);
timer = null;
}, delay);
}
};
}
完整版(支持立即执行和延迟执行)
function throttle(fn, delay, immediate = true) {
let timer = null;
let lastTime = 0;
return function(...args) {
const context = this;
const now = Date.now();
if (immediate) {
// 时间戳版本
if (now - lastTime >= delay) {
fn.apply(context, args);
lastTime = now;
}
} else {
// 定时器版本
if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args);
timer = null;
}, delay);
}
}
};
}
3. 函数柯里化 (Currying)
基础版本
function curry(fn) {
return function curried(...args) {
// 如果参数够了,直接执行
if (args.length >= fn.length) {
return fn.apply(this, args);
}
// 否则返回新函数,继续收集参数
return function(...moreArgs) {
return curried.apply(this, args.concat(moreArgs));
};
};
}
// 使用示例
function sum(a, b, c) {
return a + b + c;
}
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3)); // 6
console.log(curriedSum(1, 2)(3)); // 6
console.log(curriedSum(1)(2, 3)); // 6
高级版本(支持占位符)
function curry(fn) {
return function curried(...args) {
// 检查是否满足调用条件
const isEnough = args.length >= fn.length &&
!args.slice(0, fn.length).includes(curry.placeholder);
if (isEnough) {
return fn.apply(this, args);
}
return function(...moreArgs) {
const merged = [];
let moreIndex = 0;
// 合并参数,用新参数替换占位符
for (let i = 0; i < args.length; i++) {
if (args[i] === curry.placeholder && moreIndex < moreArgs.length) {
merged.push(moreArgs[moreIndex++]);
} else {
merged.push(args[i]);
}
}
// 添加剩余的新参数
while (moreIndex < moreArgs.length) {
merged.push(moreArgs[moreIndex++]);
}
return curried.apply(this, merged);
};
};
}
curry.placeholder = Symbol('placeholder');
// 使用示例
const join = (a, b, c) => `${a}_${b}_${c}`;
const curriedJoin = curry(join);
const _ = curry.placeholder;
console.log(curriedJoin(1, _, 3)(2)); // '1_2_3'
console.log(curriedJoin(_, 2)(1, 3)); // '1_2_3'
实用柯里化函数
// 简化版,更常用
const curry = (fn) => {
const curried = (...args) => {
if (args.length >= fn.length) {
return fn(...args);
}
return (...next) => curried(...args, ...next);
};
return curried;
};
// 实际应用:日志函数
const log = curry((level, time, message) => {
console.log(`[${level}] ${time}: ${message}`);
});
const errorLog = log('ERROR');
const infoLog = log('INFO');
errorLog('2024-01-01', '出错了'); // [ERROR] 2024-01-01: 出错了
这些实现都是面试中常见的手写题,理解它们的原理和区别很重要:
- 防抖:频繁触发只执行最后一次
- 节流:固定时间内只执行一次
- 柯里化:将多参数函数转换为单参数函数链