函数包装是指在现有函数周围创建一个新的函数,通常用于在不修改原始函数的情况下扩展或修改其行为。这是一种常见的编程模式,也称为"函数装饰"或"函数包装器"。
function originalFunction(a, b) {
return a + b;
}
function wrapperFunction(a, b) {
console.log('Arguments:', a, b);
const result = originalFunction(a, b);
console.log('Result:', result);
return result;
}
function withLogging(fn) {
return function(...args) {
console.log('Calling function with args:', args);
const result = fn.apply(this, args);
console.log('Function returned:', result);
return result;
};
}
const loggedAdd = withLogging(originalFunction);
// 原始点击处理
$('#button').click(function() {
console.log('Button clicked');
});
// 包装后的点击处理
$('#button').click(function() {
console.log('Before click');
const result = originalClickHandler.apply(this, arguments);
console.log('After click');
return result;
});
this
上下文丢失原因:直接调用包装函数时,this
可能指向错误的对象。
解决方案:使用 .apply()
或 .call()
保持上下文。
function wrapper(fn) {
return function() {
// 保持原始 this 上下文
return fn.apply(this, arguments);
};
}
原因:包装函数默认没有名称。
解决方案:使用 Object.defineProperty
设置函数名。
function wrapper(fn) {
const wrapped = function() {
return fn.apply(this, arguments);
};
Object.defineProperty(wrapped, 'name', { value: `wrapped_${fn.name}` });
return wrapped;
}
原因:普通包装器无法正确处理 Promise。
解决方案:特殊处理异步函数。
function asyncWrapper(fn) {
return async function(...args) {
console.log('Start async operation');
try {
const result = await fn.apply(this, args);
console.log('Async operation succeeded');
return result;
} catch (error) {
console.error('Async operation failed', error);
throw error;
}
};
}
function withCache(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log('Returning cached result');
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
function withRetry(fn, maxRetries = 3) {
return async function(...args) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await fn.apply(this, args);
} catch (error) {
lastError = error;
console.log(`Attempt ${i + 1} failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
throw lastError;
};
}
函数包装是一种强大的技术,可以帮助你编写更干净、更模块化的代码,同时保持核心功能的纯净性。