运行 调用任何其他 class 函数时的函数

Run a function when any other class function is called

我有一个 es6 class 有超过 100 个功能。
我添加了一个新函数,每次调用任何其他函数时我都必须 运行。
我找到了很多解决方案,但没有人解决我的问题...
我知道我可以将此调用添加到每个函数,但我将以代码重复和所有函数的大量工作结束。

那就是我到目前为止所做的:

return class Test {
    constructor () {}

    addedFN() {
         console.log('FN');
    }

    A() {
        this.addedFN();
        console.log('A');
    }

    B() {
        this.addedFN();
        console.log('B');
    }

    C() {
        this.addedFN();
        console.log('C');
    }
    ...
}

正如我们在这种方法中看到的那样,我有很多我想避免的代码重复...
另外,先调用新函数再在内部调用下一个函数的方案也不行,因为所有的函数在应用中很多地方都要用到。

我还尝试 运行 构造函数中的以下代码,但这是一个糟糕的解决方案,因为我的 class 被调用了很多次,然后这段代码每次都会 运行 :

for(const key of Object.getOwnPropertyNames(Test.prototype)) {
 const old = Test.prototype[key];
 Test.prototype[key] = function(...args) {
   addedFN(...args);
   old.call(this, ...args);
 };
}

不要运行在构造函数中,运行在class声明之后

此外,该解决方案将导致过多的递归,因为它也适用于 addedFn,后者将调用 addedFn,后者将调用 addedFn ... 等等,直到您的浏览器抛出“太多递归”错误

所以你也不想把它应用到 addedFn

在下面的代码中,我也没有将它应用于 constructor ...但是,即使我这样做了,它也不会在构造对象时调用 addedFn,所以,为了更清楚,我明确地不触及构造函数

class Test {
  constructor(y) {
    this.y = y
  }
  fun1(x) {
    console.log('I am fun1', x, this.y);
  }
  fun2() {
    console.log('I am fun2');
  }
  addedFn(...x) {
    console.log('added function', ...x);
  }
}
Object.getOwnPropertyNames(Test.prototype).forEach(f => {
  if (f !== 'constructor' && f !== 'addedFn' && typeof Test.prototype[f] === 'function') {
    const old = Test.prototype[f];
    Test.prototype[f] = function(...args) {
      this.addedFn(...args);
      return old.apply(this, args);
    }
  }
})
let c = new Test('works');
c.fun1(1,2,3)

注意:您还应该测试 Test.prototype[f] 是否是一个函数 - 除非您的 class 只有函数

这可能是 Proxy 的一个很好的用途,可以透明地更改您的对象而无需实际更改对象,只需将其包装在代理中即可。

一个get trap can check if you are accessing a method and then dynamically wrap it into a proxy with an apply trap.

class Test {
    A() { console.log('A'); }
    B() { console.log('B'); }
    C() { console.log('C'); }
    get X() { return 1; }
    Y = 2;
}

function addedFN() { console.log('FN'); }

const loggingHandler = {
  apply() {
    addedFN();
    return Reflect.apply(...arguments);
  }
};

const wrapMethods = {
  get() {
    const result = Reflect.get(...arguments);
    if (typeof result === "function")
      return new Proxy(result, loggingHandler);
    return result;
  }
};

const test = new Test();
const foo = new Proxy(test, wrapMethods);

foo.A();
foo.B();
foo.C();
console.log(foo.X);
console.log(foo.Y);
.as-console-wrapper { max-height: 100% !important; }

该方法可以被概括为接受不同的方法修饰符并将它们缓存在 WeakMap

class Test {
    A() { console.log('A'); }
    B() { console.log('B'); }
    C() { console.log('C'); }
    get X() { return 1; }
    Y = 2;
}

function addedFN() { console.log('FN'); }

const noop = () => {};
const wrapFunction = ({before = noop, after = noop}) => ({
  apply() {
    before();
    const result = Reflect.apply(...arguments);
    after();
    return result;
  }
});

const wrapMethods = handler => ({
  _cache: new WeakMap(),
  get() {
    const result = Reflect.get(...arguments);
    if (typeof result === "function") {
      if (!this._cache.has(result))
        this._cache.set(result, new Proxy(result, handler));
        
      return this._cache.get(result);
    }
    
    return result;
  }
});

const loggingHandler = wrapFunction({before: addedFN});

const test = new Test();
const foo = new Proxy(test, wrapMethods(loggingHandler));

foo.A();
foo.B();
foo.C();
console.log(foo.X);
console.log(foo.Y);