You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript对象日志记录器实现咨询:装饰器方案是否可行?

Absolutely! Using a decorator (or a proxy-based approach, which is a modern, flexible alternative) is exactly the right strategy for building this logger. Let’s break down why, and walk through concrete implementations:

Why This Approach Works

Decorators and proxies let you wrap an existing object without modifying its original code—this follows the open/closed principle (open for extension, closed for modification). They preserve the original object’s behavior while injecting your logging logic seamlessly, which is perfect for your use case.

Implementation 1: Proxy-Based Logger (Best for Most Cases)

ES6 Proxies are ideal here because they can intercept all property accesses (including method calls) on both custom and built-in objects. This avoids having to manually iterate over methods or handle prototype chain quirks:

function createLoggingProxy(target) {
  return new Proxy(target, {
    get(target, prop, receiver) {
      const originalValue = Reflect.get(target, prop, receiver);
      
      // Only wrap function properties (methods)
      if (typeof originalValue === 'function') {
        return function(...args) {
          // Log the method call details
          console.log(`[LOG] Called method: "${String(prop)}"`);
          console.log(`[LOG] Arguments:`, args);
          
          // Execute the original method with correct context
          const result = originalValue.apply(this === receiver ? target : this, args);
          
          // Log the return value
          console.log(`[LOG] Method "${String(prop)}" returned:`, result);
          return result;
        };
      }
      
      // Return non-function properties as-is
      return originalValue;
    }
  });
}

Example Usage

Custom Object

const myService = {
  calculateTotal(items) {
    return items.reduce((sum, item) => sum + item.price, 0);
  },
  sendNotification(message) {
    return `Notification sent: ${message}`;
  }
};

const loggedService = createLoggingProxy(myService);
loggedService.calculateTotal([{ price: 10 }, { price: 20 }]);
// Logs:
// [LOG] Called method: "calculateTotal"
// [LOG] Arguments: [{ price: 10 }, { price: 20 }]
// [LOG] Method "calculateTotal" returned: 30

Built-In Object

Proxies work seamlessly with built-ins like arrays or dates:

const loggedArray = createLoggingProxy(['apple', 'banana']);
loggedArray.push('cherry');
// Logs:
// [LOG] Called method: "push"
// [LOG] Arguments: ["cherry"]
// [LOG] Method "push" returned: 3

console.log(loggedArray); // Still outputs: ["apple", "banana", "cherry"]

Implementation 2: Traditional Decorator Function

If you need to support older environments without Proxy support, a manual decorator that wraps each method works well for custom objects:

function logDecorator(target) {
  // Iterate over all own enumerable properties of the target
  for (const prop of Object.keys(target)) {
    const originalMethod = target[prop];
    
    if (typeof originalMethod === 'function') {
      target[prop] = function(...args) {
        console.log(`[LOG] Called method: "${prop}" with args:`, args);
        const result = originalMethod.apply(this, args);
        console.log(`[LOG] Method "${prop}" returned:`, result);
        return result;
      };
    }
  }
  return target;
}

Note: This won’t automatically handle methods on the prototype chain (like built-in array methods), so it’s better suited for custom objects with own methods.

Alternative Approaches

  • Class Decorators: If your objects are instances of ES6 classes, you can decorate the class itself to add logging to all prototype methods:

    function logClass(target) {
      for (const prop of Object.getOwnPropertyNames(target.prototype)) {
        const originalMethod = target.prototype[prop];
        if (typeof originalMethod === 'function' && prop !== 'constructor') {
          target.prototype[prop] = function(...args) {
            console.log(`[LOG] Class method "${prop}" called with args:`, args);
            const result = originalMethod.apply(this, args);
            console.log(`[LOG] Class method "${prop}" returned:`, result);
            return result;
          };
        }
      }
      return target;
    }
    
    // Usage
    @logClass
    class Calculator {
      add(a, b) { return a + b; }
      multiply(a, b) { return a * b; }
    }
    
    const calc = new Calculator();
    calc.add(5, 3); // Logs method call and result
    
  • Manual Method Wrapping: For one-off cases, you could manually wrap individual methods, but this is not scalable for objects with many methods.

Final Recommendation

Go with the Proxy-based approach if you can support modern browsers—it’s the most flexible, handles both custom and built-in objects, and requires minimal code. The traditional decorator is a solid fallback for older environments or custom-only objects.

内容的提问来源于stack exchange,提问作者Ratcat71

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:27:28