【问题标题】:JavaScript Equivalent Of PHP __callJavaScript 等价于 PHP __call
【发布时间】:2012-04-19 10:20:43
【问题描述】:

在 PHP 中,您可以使用“魔术”__call 函数检测何时调用了一个方法,即使该方法不存在。

public function __call($methodName, $args)
{
    // do something
}

您可以调用任何方法,并将名称和参数传递给这个神奇的包罗万象。

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

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

foo.nonExistent();

【问题讨论】:

标签: javascript


【解决方案1】:

使用 ES6 Proxy 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'));

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

请参阅this answer 以更全面地了解 Proxy。

【讨论】:

  • 小说明 - 代理不允许您精确模拟 PHP 的 __call。 PHP 的__call__get 区分作为方法调用的属性与作为属性访问的属性。相比之下,Proxy 融合了这些概念。所有这一切意味着你不能使用 Proxy 来选择性地将属性定义为函数,只有当它们被调用时。您只能提供所有方法,或检查属性名称并有选择地提供功能。
【解决方案2】:

【讨论】:

  • 嗨@rohk 感谢您的回答。我将不得不尝试一种稍微不同的策略来满足我的需要,但信息很好。谢谢。
【解决方案3】:

以@amirnissim 的回答为基础。

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

模仿 PHP 的魔法方法

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

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)
    }
}

这实质上为您提供了与 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?") }

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

let myProxy = new MyProxy()

myProxy.test()
myProxy.hello()
myProxy.helloWorld()

上面的例子输出:

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 函数路由到target 对象上的get 方法?

请记住,这仅适用于 ES6。 Transpilers such as Babel不能,and I quote

由于 ES5 的限制,不能对代理进行转译或 polyfill。

只要满足此条件,上述解决方案就可以完美运行。例如,在Node.js 中,这是一个完全可行的选择。

【讨论】:

    【解决方案4】:

    没有。由于 JavaScript 的工作方式,相当于 Python 的 __getattr__/__getitem__,而不是 PHP 的 __call,因为它需要在检索属性时而不是在调用它时处理。

    然后,您可以查看Python's __getattr__ in Javascript 之类的问题,它以这种方式回答。

    另请参阅以下问题:

    【讨论】:

      【解决方案5】:

      虽然这不是一种优雅的方式,因为我们已经推断出 JavaScript 没有 __callmethod_missing__getattr__,但可以创建属性组合来创建中继到单个方法的具体函数,传递用于创建它的属性。

      一个例子是Myriad.js

      【讨论】:

        猜你喜欢
        • 2011-05-29
        • 2011-07-23
        • 1970-01-01
        • 2010-09-29
        • 2015-02-10
        • 2020-07-27
        • 2011-11-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多