JavaScript實現(xiàn)函數(shù)執(zhí)行攔截的多種方法
1. 基礎(chǔ)攔截方法
1.1 函數(shù)包裝(最簡單的攔截方式)
function originalFunction(a, b) {
console.log('執(zhí)行原始函數(shù)');
return a + b;
}
// 創(chuàng)建包裝函數(shù)實現(xiàn)攔截
function createInterceptor(originalFn) {
return function(...args) {
console.log(`函數(shù)被調(diào)用,參數(shù): ${args}`);
const start = performance.now();
try {
const result = originalFn.apply(this, args);
console.log(`函數(shù)返回: ${result}`);
return result;
} catch (error) {
console.error(`函數(shù)拋出異常: ${error}`);
throw error;
} finally {
const duration = performance.now() - start;
console.log(`函數(shù)執(zhí)行耗時: ${duration}ms`);
}
};
}
const interceptedFn = createInterceptor(originalFunction);
interceptedFn(2, 3); // 輸出調(diào)用日志和結(jié)果
1.2 對象方法攔截
const obj = {
value: 10,
add(x) {
return this.value + x;
}
};
// 攔截對象方法
function interceptMethod(obj, methodName) {
const original = obj[methodName];
obj[methodName] = function(...args) {
console.log(`攔截方法 ${methodName} 調(diào)用`);
return original.apply(this, args);
};
}
interceptMethod(obj, 'add');
obj.add(5); // 輸出: "攔截方法 add 調(diào)用" 然后返回15
2. 高級攔截技術(shù)
2.1 使用Proxy實現(xiàn)全面攔截
const target = {
id: 42,
compute(a, b) {
return a * b;
}
};
const handler = {
// 攔截函數(shù)調(diào)用
apply(target, thisArg, argumentsList) {
console.log(`調(diào)用函數(shù): ${target.name}`, argumentsList);
const result = Reflect.apply(target, thisArg, argumentsList);
console.log(`函數(shù)返回: ${result}`);
return result;
},
// 攔截屬性訪問
get(target, prop, receiver) {
const value = Reflect.get(target, prop, receiver);
if (typeof value === 'function') {
return new Proxy(value, handler); // 對方法返回新代理
}
return value;
}
};
const proxy = new Proxy(target, handler);
proxy.compute(6, 7); // 輸出調(diào)用日志和結(jié)果
2.2 基于裝飾器的攔截(ES2016+)
// 類方法裝飾器
function logExecution(target, name, descriptor) {
const original = descriptor.value;
descriptor.value = function(...args) {
console.log(`調(diào)用方法 ${name} 參數(shù):`, args);
try {
const result = original.apply(this, args);
console.log(`方法 ${name} 返回:`, result);
return result;
} catch (error) {
console.error(`方法 ${name} 出錯:`, error);
throw error;
}
};
return descriptor;
}
class Calculator {
@logExecution
add(a, b) {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3); // 輸出調(diào)用日志和結(jié)果
3. 應(yīng)用場景實現(xiàn)
3.1 性能監(jiān)控攔截器
function createPerformanceInterceptor(fn, metricName) {
return function(...args) {
const start = performance.now();
const memoryBefore = process.memoryUsage().heapUsed;
try {
const result = fn.apply(this, args);
const duration = performance.now() - start;
const memoryAfter = process.memoryUsage().heapUsed;
console.log(`[Metrics] ${metricName} - 耗時: ${duration.toFixed(2)}ms`);
console.log(`[Metrics] ${metricName} - 內(nèi)存變化: ${((memoryAfter - memoryBefore) / 1024).toFixed(2)}KB`);
return result;
} catch (error) {
console.error(`[Metrics] ${metricName} - 執(zhí)行失敗`);
throw error;
}
};
}
// 使用示例
const monitoredFn = createPerformanceInterceptor(heavyCalculation, 'heavyCalculation');
monitoredFn(/* 參數(shù) */);
3.2 權(quán)限控制攔截器
function createAuthInterceptor(fn, requiredRole) {
return function(...args) {
const user = getCurrentUser(); // 假設(shè)有獲取當前用戶的函數(shù)
if (!user || !user.roles.includes(requiredRole)) {
throw new Error(`無權(quán)訪問此功能,需要角色: ${requiredRole}`);
}
console.log(`用戶 ${user.name} (${user.roles}) 調(diào)用受保護函數(shù)`);
return fn.apply(this, args);
};
}
// 使用示例
const adminOnlyFunction = createAuthInterceptor(deleteAllData, 'admin');
try {
adminOnlyFunction();
} catch (error) {
console.error(error.message);
}
4. 特殊函數(shù)攔截
4.1 構(gòu)造函數(shù)攔截
function interceptConstructor(OriginalClass) {
return new Proxy(OriginalClass, {
construct(target, args) {
console.log(`創(chuàng)建 ${target.name} 實例,參數(shù):`, args);
const instance = Reflect.construct(target, args);
// 返回代理實例以攔截方法調(diào)用
return new Proxy(instance, {
get(target, prop) {
const value = Reflect.get(target, prop);
if (typeof value === 'function') {
console.log(`調(diào)用實例方法 ${prop}`);
}
return value;
}
});
}
});
}
// 使用示例
class User {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, ${this.name}!`;
}
}
const ProxiedUser = interceptConstructor(User);
const user = new ProxiedUser('Alice'); // 輸出構(gòu)造函數(shù)日志
user.greet(); // 輸出方法調(diào)用日志
4.2 異步函數(shù)攔截
function interceptAsync(fn) {
return async function(...args) {
console.log('異步函數(shù)開始執(zhí)行');
try {
const result = await fn.apply(this, args);
console.log('異步函數(shù)成功完成');
return result;
} catch (error) {
console.error('異步函數(shù)執(zhí)行失敗:', error);
throw error;
}
};
}
// 使用示例
const originalAsync = async (delay) => {
await new Promise(resolve => setTimeout(resolve, delay));
return '完成';
};
const interceptedAsync = interceptAsync(originalAsync);
interceptedAsync(1000).then(console.log);
5. 高級應(yīng)用:AOP實現(xiàn)
5.1 面向切面編程基礎(chǔ)實現(xiàn)
// 切面注冊表
const aspectRegistry = new Map();
// 注冊切面
function registerAspect(fnName, { before, after, around, onError }) {
aspectRegistry.set(fnName, { before, after, around, onError });
}
// 應(yīng)用切面
function applyAspects(obj) {
for (const [name, method] of Object.entries(obj)) {
if (typeof method === 'function' && aspectRegistry.has(name)) {
const aspects = aspectRegistry.get(name);
obj[name] = createAspectWrapper(method, aspects);
}
}
}
function createAspectWrapper(original, aspects) {
return function(...args) {
try {
// 前置通知
if (aspects.before) aspects.before(args);
// 環(huán)繞通知
let result;
if (aspects.around) {
result = aspects.around(() => original.apply(this, args));
} else {
result = original.apply(this, args);
}
// 后置通知
if (aspects.after) aspects.after(result);
return result;
} catch (error) {
// 異常通知
if (aspects.onError) aspects.onError(error);
throw error;
}
};
}
// 使用示例
const service = {
calculate(a, b) {
return a + b;
}
};
// 注冊切面
registerAspect('calculate', {
before: (args) => console.log('計算開始,參數(shù):', args),
after: (result) => console.log('計算完成,結(jié)果:', result)
});
// 應(yīng)用切面
applyAspects(service);
service.calculate(2, 3); // 輸出切面日志
5.2 基于ES6 Proxy的AOP實現(xiàn)
function createAopProxy(target, aspects = {}) {
return new Proxy(target, {
get(target, prop) {
const original = target[prop];
if (typeof original === 'function') {
return function(...args) {
// 前置通知
if (aspects.before) aspects.before(prop, args);
try {
// 環(huán)繞通知
let result;
if (aspects.around) {
result = aspects.around(prop, () => original.apply(target, args));
} else {
result = original.apply(target, args);
}
// 后置通知
if (aspects.after) aspects.after(prop, result);
return result;
} catch (error) {
// 異常通知
if (aspects.onError) aspects.onError(prop, error);
throw error;
}
};
}
return original;
}
});
}
// 使用示例
const service = {
add(a, b) { return a + b; },
multiply(a, b) { return a * b; }
};
const proxiedService = createAopProxy(service, {
before: (method, args) => console.log(`調(diào)用 ${method},參數(shù): ${args}`),
after: (method, result) => console.log(`方法 ${method} 返回: ${result}`)
});
proxiedService.add(2, 3); // 輸出調(diào)用日志和結(jié)果
proxiedService.multiply(4, 5); // 輸出調(diào)用日志和結(jié)果
6. 注意事項與最佳實踐
性能考量:
- 避免在熱路徑(hot path)上使用復(fù)雜的攔截邏輯
- 生產(chǎn)環(huán)境考慮移除不必要的攔截器
- 使用
performance.now()測量攔截開銷
調(diào)試技巧:
- 為攔截器添加唯一標識便于調(diào)試
- 保留原始函數(shù)引用以便直接調(diào)用
- 使用
console.trace()追蹤攔截調(diào)用棧
安全注意事項:
- 確保攔截器不會意外暴露敏感數(shù)據(jù)
- 驗證攔截器中的權(quán)限檢查邏輯
- 避免在攔截器中修改原始參數(shù)
最佳實踐:
- 保持攔截邏輯單一職責
- 為攔截器編寫單元測試
- 文檔化攔截行為和使用方式
7. 現(xiàn)代JavaScript攔截方案
7.1 使用Reflect API
const handler = {
apply(target, thisArg, argumentsList) {
console.log(`調(diào)用: ${target.name}`, argumentsList);
return Reflect.apply(target, thisArg, argumentsList);
},
construct(target, argumentsList, newTarget) {
console.log(`構(gòu)造: ${target.name}`, argumentsList);
return Reflect.construct(target, argumentsList, newTarget);
}
};
function sum(a, b) { return a + b; }
const proxiedSum = new Proxy(sum, handler);
proxiedSum(2, 3); // 輸出調(diào)用日志
7.2 使用Async/Await攔截
function asyncInterceptor(target) {
return async function(...args) {
const start = Date.now();
try {
const result = await target.apply(this, args);
console.log(`異步操作成功,耗時: ${Date.now() - start}ms`);
return result;
} catch (error) {
console.error(`異步操作失敗,耗時: ${Date.now() - start}ms`, error);
throw error;
}
};
}
// 使用示例
const originalFetch = fetch;
window.fetch = asyncInterceptor(originalFetch);
// 所有fetch調(diào)用都會被攔截
fetch('https://api.example.com/data')
.then(response => response.json())
.then(console.log);
8. 實際應(yīng)用案例
8.1 Redux中間件(函數(shù)攔截的典型應(yīng)用)
// 自定義Redux中間件
const loggerMiddleware = store => next => action => {
console.groupCollapsed(`dispatching ${action.type}`);
console.log('prev state:', store.getState());
console.log('action:', action);
const result = next(action);
console.log('next state:', store.getState());
console.groupEnd();
return result;
};
// 應(yīng)用中間件
const store = createStore(
rootReducer,
applyMiddleware(loggerMiddleware)
);
8.2 Express中間件(Node.js中的攔截)
// 請求日志中間件
function requestLogger(req, res, next) {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.originalUrl} - ${res.statusCode} (${duration}ms)`);
});
next();
}
// 錯誤處理中間件
function errorHandler(err, req, res, next) {
console.error('發(fā)生錯誤:', err.stack);
res.status(500).send('服務(wù)器錯誤');
}
// 使用中間件
app.use(requestLogger);
app.use(errorHandler);
通過以上技術(shù)方案,您可以在JavaScript中實現(xiàn)各種級別的函數(shù)執(zhí)行攔截,滿足不同場景下的需求。從簡單的日志記錄到復(fù)雜的AOP編程,選擇適合您項目需求的攔截策略,并注意平衡功能與性能的關(guān)系。
以上就是JavaScript實現(xiàn)函數(shù)執(zhí)行攔截的多種方法的詳細內(nèi)容,更多關(guān)于JavaScript函數(shù)執(zhí)行攔截的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript實現(xiàn)uploadify上傳格式以及個數(shù)限制
這篇文章主要介紹了javascript如何限制uploadify上傳格式以及個數(shù)的實現(xiàn)方法,感興趣的小伙伴們可以參考一下2015-11-11
removeEventListener事件不起作用的問題及解決
文章指出在使用JavaScript的removeEventListener移除事件監(jiān)聽器時,應(yīng)使用外部函數(shù)而非匿名函數(shù),并強調(diào)了addEventListener和removeEventListener使用時的一些注意事項,例如在IE中使用attachEvent和detachEvent時不要傳入匿名函數(shù)2026-03-03
layer實現(xiàn)登錄彈框,登錄成功后關(guān)閉彈框并調(diào)用父窗口的例子
今天小編就為大家分享一篇layer實現(xiàn)登錄彈框,登錄成功后關(guān)閉彈框并調(diào)用父窗口的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09

