PHP等效的PHP __call

Fen*_*ton 28 javascript

在PHP中,您可以使用"魔术" __call功能检测方法何时被调用,即使它不存在.

public function __call($methodName, $args)
{
    // do something
}
Run Code Online (Sandbox Code Playgroud)

您可以调用任何方法,并将名称和参数传递给此魔法catch-all.

在JavaScript中是否有类似的技术允许调用任何方法,即使它实际上不存在于对象上?

var foo = (function () {
    return {
         __call: function (name, args) { // NOT REAL CODE
             alert(name); // "nonExistent"
         }
    }
}());

foo.nonExistent();
Run Code Online (Sandbox Code Playgroud)

ami*_*sim 23

使用ES6可能代理API:

var myObj = {};
var myProxy = new Proxy(myObj, {
  get: function get(target, name) {
    return function wrapper() {
      var args = Array.prototype.slice.call(arguments);
      console.log(args[0]);
      return "returns: " + args[0];
    }
  }
});
console.log(myProxy.foo('bar'));
Run Code Online (Sandbox Code Playgroud)

MDN上提供了浏览器兼容性.截至2017年8月,除Internet Explorer之外的所有浏览器(包括Microsoft Edge)都支持它.

有关Proxy的详细信息,请参阅此答案.


D. *_*aro 5

稍微建立在@amirnissim 的回答之上。

正如我们大多数人可能已经知道的那样,ES6 引入了 Proxy API,它允许我们创建一个对象(代理对象)来捕获对该对象的调用,从而我们有机会“路由”用户调用的属性任何我们可能希望的对象。

模仿 PHP 的魔法方法

不幸的是,没有办法使用 Proxy 对象扩展类,但我们可以做的是设置一个中间步骤,将对象转换为代理,并将任何传入的方法调用路由到对象本身可用的方法:

class MyProxy
{
    constructor ()
    {
        return this.asProxy()
    }

    /**
     * Return as a proxy with this object as its target.
     */
    asProxy ()
    {
        let handler = {
            /**
             * This function is called whenever any property on the Proxy 
             * is called.
             * 
             * @param target the "parent" object; the object the proxy 
             *        virtualizes
             * @param prop the property called on the Proxy
             */
            get: function (target, prop)
            {
                /* This will return the property on the "parent" object
                 */
                if (typeof target[prop] !== 'undefined')
                    return target[prop]

                // TODO: implement custom logic
            }
        }

        return new Proxy(this, handler)
    }
}
Run Code Online (Sandbox Code Playgroud)

这本质上同时为您提供了与 PHP 的魔法__get方法和__call方法相同的功能。至于__call版本,我们只是返回一个函数供用户输入参数。

证明以上

为了使用这个,让我们首先在TODO: implement custom logic驻留的地方添加一些自定义逻辑:

if (prop === 'helloWorld')
    return function () { console.log("Hello, world!") }
else
    return function () { console.log("Where art thou, hello world?") }
Run Code Online (Sandbox Code Playgroud)

如果我们继续创建类的新实例MyProxy,我们可以触发我们实现的自定义逻辑:

let myProxy = new MyProxy()

myProxy.test()
myProxy.hello()
myProxy.helloWorld()
Run Code Online (Sandbox Code Playgroud)

上面的例子输出:

Where art thou, hello world?
Where art thou, hello world?
Hello, world!

当然,也可以从get函数返回任何其他类型的值,我们也可以返回字符串或整数。

便于使用; 通过继承使用

为了使其更易于使用,我是否建议将该asProxy方法包装到另一个类中,然后简单地使用包含该asProxy方法的类扩展任何需要“魔术方法”功能的类?通过简单地asProxy从构造函数返回该方法,您基本上可以获得与在 PHP 和 JavaScript 中看到的相同的功能。

当然,还需要某种get method程度的可编辑性,以便仍然可以从子类处理自定义逻辑。也许通过向return this.asProxy(() => {})然后从get函数本身调用的 发送一个闭包?或者甚至可能将get函数路由到对象get上存在的方法target

但是请记住,这仅适用于 ES6。像 Babel 这样的转译器不能,我引用

由于 ES5 的限制,代理不能被转译或填充。

然而,只要满足这个条件,上面提出的解决方案就可以很好地工作。例如,它是Node.js 中一个非常可行的选项。