javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > JavaScript函数执行拦截

JavaScript实现函数执行拦截的多种方法

作者:北辰alk

函数执行拦截是JavaScript中强大的元编程技术,可以实现日志记录、性能监控、权限控制等功能,本文将深入探讨多种函数拦截方法,从基础到高级,帮助您全面掌握这一技术,需要的朋友可以参考下

1. 基础拦截方法

1.1 函数包装(最简单的拦截方式)

function originalFunction(a, b) {
  console.log('执行原始函数');
  return a + b;
}

// 创建包装函数实现拦截
function createInterceptor(originalFn) {
  return function(...args) {
    console.log(`函数被调用,参数: ${args}`);
    const start = performance.now();
    
    try {
      const result = originalFn.apply(this, args);
      console.log(`函数返回: ${result}`);
      return result;
    } catch (error) {
      console.error(`函数抛出异常: ${error}`);
      throw error;
    } finally {
      const duration = performance.now() - start;
      console.log(`函数执行耗时: ${duration}ms`);
    }
  };
}

const interceptedFn = createInterceptor(originalFunction);
interceptedFn(2, 3); // 输出调用日志和结果

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} 调用`);
    return original.apply(this, args);
  };
}

interceptMethod(obj, 'add');
obj.add(5); // 输出: "拦截方法 add 调用" 然后返回15

2. 高级拦截技术

2.1 使用Proxy实现全面拦截

const target = {
  id: 42,
  compute(a, b) {
    return a * b;
  }
};

const handler = {
  // 拦截函数调用
  apply(target, thisArg, argumentsList) {
    console.log(`调用函数: ${target.name}`, argumentsList);
    const result = Reflect.apply(target, thisArg, argumentsList);
    console.log(`函数返回: ${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); // 输出调用日志和结果

2.2 基于装饰器的拦截(ES2016+)

// 类方法装饰器
function logExecution(target, name, descriptor) {
  const original = descriptor.value;
  
  descriptor.value = function(...args) {
    console.log(`调用方法 ${name} 参数:`, 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); // 输出调用日志和结果

3. 应用场景实现

3.1 性能监控拦截器

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} - 内存变化: ${((memoryAfter - memoryBefore) / 1024).toFixed(2)}KB`);
      
      return result;
    } catch (error) {
      console.error(`[Metrics] ${metricName} - 执行失败`);
      throw error;
    }
  };
}

// 使用示例
const monitoredFn = createPerformanceInterceptor(heavyCalculation, 'heavyCalculation');
monitoredFn(/* 参数 */);

3.2 权限控制拦截器

function createAuthInterceptor(fn, requiredRole) {
  return function(...args) {
    const user = getCurrentUser(); // 假设有获取当前用户的函数
    
    if (!user || !user.roles.includes(requiredRole)) {
      throw new Error(`无权访问此功能,需要角色: ${requiredRole}`);
    }
    
    console.log(`用户 ${user.name} (${user.roles}) 调用受保护函数`);
    return fn.apply(this, args);
  };
}

// 使用示例
const adminOnlyFunction = createAuthInterceptor(deleteAllData, 'admin');
try {
  adminOnlyFunction();
} catch (error) {
  console.error(error.message);
}

4. 特殊函数拦截

4.1 构造函数拦截

function interceptConstructor(OriginalClass) {
  return new Proxy(OriginalClass, {
    construct(target, args) {
      console.log(`创建 ${target.name} 实例,参数:`, args);
      const instance = Reflect.construct(target, args);
      
      // 返回代理实例以拦截方法调用
      return new Proxy(instance, {
        get(target, prop) {
          const value = Reflect.get(target, prop);
          if (typeof value === 'function') {
            console.log(`调用实例方法 ${prop}`);
          }
          return value;
        }
      });
    }
  });
}

// 使用示例
class User {
  constructor(name) {
    this.name = name;
  }
  
  greet() {
    return `Hello, ${this.name}!`;
  }
}

const ProxiedUser = interceptConstructor(User);
const user = new ProxiedUser('Alice'); // 输出构造函数日志
user.greet(); // 输出方法调用日志

4.2 异步函数拦截

function interceptAsync(fn) {
  return async function(...args) {
    console.log('异步函数开始执行');
    try {
      const result = await fn.apply(this, args);
      console.log('异步函数成功完成');
      return result;
    } catch (error) {
      console.error('异步函数执行失败:', 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. 高级应用:AOP实现

5.1 面向切面编程基础实现

// 切面注册表
const aspectRegistry = new Map();

// 注册切面
function registerAspect(fnName, { before, after, around, onError }) {
  aspectRegistry.set(fnName, { before, after, around, onError });
}

// 应用切面
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);
      
      // 环绕通知
      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('计算开始,参数:', args),
  after: (result) => console.log('计算完成,结果:', result)
});

// 应用切面
applyAspects(service);

service.calculate(2, 3); // 输出切面日志

5.2 基于ES6 Proxy的AOP实现

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 {
            // 环绕通知
            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(`调用 ${method},参数: ${args}`),
  after: (method, result) => console.log(`方法 ${method} 返回: ${result}`)
});

proxiedService.add(2, 3); // 输出调用日志和结果
proxiedService.multiply(4, 5); // 输出调用日志和结果

6. 注意事项与最佳实践

性能考量

调试技巧

安全注意事项

最佳实践

7. 现代JavaScript拦截方案

7.1 使用Reflect API

const handler = {
  apply(target, thisArg, argumentsList) {
    console.log(`调用: ${target.name}`, argumentsList);
    return Reflect.apply(target, thisArg, argumentsList);
  },
  construct(target, argumentsList, newTarget) {
    console.log(`构造: ${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); // 输出调用日志

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调用都会被拦截
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(console.log);

8. 实际应用案例

8.1 Redux中间件(函数拦截的典型应用)

// 自定义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;
};

// 应用中间件
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('发生错误:', err.stack);
  res.status(500).send('服务器错误');
}

// 使用中间件
app.use(requestLogger);
app.use(errorHandler);

通过以上技术方案,您可以在JavaScript中实现各种级别的函数执行拦截,满足不同场景下的需求。从简单的日志记录到复杂的AOP编程,选择适合您项目需求的拦截策略,并注意平衡功能与性能的关系。

以上就是JavaScript实现函数执行拦截的多种方法的详细内容,更多关于JavaScript函数执行拦截的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文