拦截 JavaScript 中的方法调用

7

PHP中__call魔术方法的等效方法是什么?

我曾经认为代理可以做到这一点,但实际上不行。

class MyClass{
  constructor(){
    return new Proxy(this, {
      apply: function(target, thisArg, args){
        console.log('call', thisArg, args);
        return 'test';
      },

      get: function(target, prop){
        console.log('get', prop, arguments);
      }


    });

  }

}

var inst = new MyClass();
console.log(inst.foo(123));

get 看起来是有效的,因为我看到了 "get foo",但是 apply 不行。我得到了一个 "is not a function" 的错误。


代理上的 apply 陷阱用于调用对象本身,即 inst(…)。但是,您无法从非可调用目标创建可调用代理。 - Bergi
你的 .foo 属性上的 get 陷阱必须返回一个函数,以便你可以调用它。 - Bergi
https://dev59.com/6Kzka4cB1Zd3GeqP3yOuJavaScript代理对象上混合构造函数和应用陷阱。https://dev59.com/NBv8s4cB2Jgan1znxERz在代理处理程序中,如何区分获取属性变量与调用方法的不同? - Bergi
3个回答

8

apply 实际上是处理对象本身的函数调用,即,如果您使用 new Proxy(someFunction, { apply: ... }),则在调用 someFunction 之前,将调用 apply

没有必要为捕获对属性的调用进行拦截,因为这将是多余的——get 已经处理了当返回一个属性时。您只需返回一个函数,然后在调用该函数时生成一些调试输出即可。

class MyClass{
  constructor(){
    return new Proxy(this, {
      get: function(target, prop) {
        return function() {
          console.log('function call', prop, arguments);
          return 42;
        };
      }
    });
  }
}

var inst = new MyClass();
console.log(inst.foo(123));


我认为这不是一个完整的解决方案。你返回了一个静态值。你需要在get内部使用apply来计算原始函数的结果。 - Badis Merabet
1
@BadisMerabet 我相信 OP 可以修改函数以适应他们的需求。尽管如此,问题是 PHP 中 __call 的类比,它用于解决对象不存在的函数,即没有“原始函数”可调用。Proxy 的默认/预期行为已经有很好的文档记录 - Aurel Bílý
谢谢您澄清这一点,现在我明白了。但我还是想知道拦截调用代码或软件中不存在的函数的目标或好处是什么? - Badis Merabet
@BadisMerabet 我知道这种“神奇”的方法(在某些语言中可与宏比拟,在其他语言中则类似于运行时反射)可以用于实现ORM系统,其中方法可以动态生成以用于一对多关系。我能想到的另一些用途包括-带有延迟加载模块/函数的库,具有(过多)具有给定模式的所有函数的库(例如几何库 - addPoint2DaddPoint3D等)。有很多应用,也许并不总是表明“好”的代码,但毕竟这是Javascript。 :) - Aurel Bílý

4
这是另一种实现您所请求的方式。

class MyClass{
  constructor(){
     return new Proxy(this, {
        get(target, propKey, receiver) {
            const origMethod = target[propKey];
            return function (...args) {
                let result = origMethod.apply(this, args);
                console.log(propKey + JSON.stringify(args)
                    + ' -> ' + JSON.stringify(result));
                return result;
            };
        }
    });
  }
  
foo = (x) => {
  return x + 1;
};

}

var inst = new MyClass();
console.log(inst.foo(123));


太棒了!这可以拦截对象的所有方法调用,即使方法实际上不存在。 - cibercitizen1
您的代码应该被标记为正确答案。它包括重定向到类方法。感谢如此干净的实现。 - Heroselohim

2

代理可以做到这一点,但即使是在捕获方法时,您也必须使用代理的get方法。

然后在这里我也执行了您的真实方法,但我不知道您是否想对其进行模拟。

class MyClass {
  constructor() {
    return new Proxy(this, {
      get(target, prop, receiver) {
        if (typeof target[prop] !== "function") {
          return "etcetcetc";
        }
        return function(...args) {
          console.log('call', args);
          return target[prop]();
        };
      }
    });
  }

  foo() {
    console.log('I am foo!');
  }
}

var inst = new MyClass();
inst.foo(123);

正如您所见,如果您正在调用实例的方法,我将拦截它,然后返回您原始的方法执行。

如果您正在访问实例的属性,则我将始终返回一个模拟字符串。

然后,当然可以根据您想要的行为进行更改。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接