33

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

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

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

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

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

foo.nonExistent();
4

5 回答 5

31

可以使用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'));

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

请参阅此答案以更完整地了解代理。

于 2013-10-22T14:42:18.497 回答
9

自 Gecko 43 (Firefox 43 / Thunderbird 43 / SeaMonkey 2.40) 以来已过时

您可以__noSuchMethod__在 Firefox 中使用。不幸的是,它是非标准的......

相关问题:是否有与 __noSuchMethod__ 特性等效的属性,或者在 JS 中实现它的方法?

于 2012-04-19T10:28:03.263 回答
5

以@amirnissim 的回答为基础。

正如我们大多数人可能已经知道的那样,ES6 引入了代理 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)
    }
}

这实质上同时为您提供了与 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函数路由到对象get上存在的方法target

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

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

但是,只要满足此条件,上述解决方案确实可以正常工作。例如,它是Node.js中一个完全可行的选项。

于 2018-02-15T17:28:18.370 回答
3

不。由于 JavaScript 的工作方式,等效项类似于 Python 的__getattr__/ __getitem__,而不是 PHP 的__call,因为它需要在检索属性时而不是在调用它时处理。

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

另请参阅以下问题:

于 2012-04-19T10:27:13.767 回答
0

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

一个例子是Myriad.js

于 2014-01-10T15:13:16.220 回答