相当于 PHP 的 JavaScript __call

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10226064/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 09:06:55  来源:igfitidea点击:

JavaScript Equivalent Of PHP __call

javascript

提问by Fenton

In PHP you can detect when a method is called even when it doesn't exist using the "magic" __callfunction.

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

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

You can call any method and the name and arguments are passed to this magic catch-all.

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

Is there a similar technique in JavaScript that would allow any method to be called even if it actually didn't exist on the object?

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

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

foo.nonExistent();

采纳答案by amirnissim

It ispossible using the ES6 Proxy API:

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

Browser compatibility is available on MDN. As of August 2017 all browsers (including Microsoft Edge) except Internet Explorer support it.

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

See this answerfor a more complete look at Proxy.

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

回答by D. Ataro

To build upon @amirnissim's answer a slight bit.

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

As most of us are probably already aware, ES6 introduces the Proxy API, which allows us to create an object (the Proxy object) that traps calls to that object, whereby we are given an opportunity to "route" the attribute the user called on the object to whatsoever we may wish.

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

Mimicking PHP's Magic Methods

模仿 PHP 的魔法方法

There is unfortuantely no way to extend a class using the Proxy object, but what we can do is set up an intermediary step to turn an object into a proxy, and route any incoming method calls to the method available on the object itself:

不幸的是,没有办法使用 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)
    }
}

This essentially gives you the same functionality to PHP's magic __getmethod and __callmethod at the same time. As for the __callversion, we are simply returning a function for the user to enter arguments into.

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

Demonstrating the Above

证明以上

In order to use this, let us first add a bit of custom logic to the place where the TODO: implement custom logicresides:

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

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

If we then go ahead and create a new instance of the MyProxyclass, we can trigger the custom logic we implemented:

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

let myProxy = new MyProxy()

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

The above example outputs:

上面的例子输出:

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

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

It would, of course, also be possible to return any other type of value from the getfunction, we could just as well return a string or an integer.

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

Ease of Use; Usage Through Inheritance

便于使用; 通过继承使用

In order to make this even easier to use, may I suggest wrapping the asProxymethod into another class, then simply extending any class that needs the "magic method" functionality with the class containing the asProxymethod? By simply returning the asProxymethod from the constructor, you are basically given the same functionality you would see in PHP, in JavaScript.

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

Of course, it would also be somewhat required that the get methodis somewhat editable so that custom logic can still be handled from the subclass. Perhaps by sending in a closure to the return this.asProxy(() => {})that is then called from the getfunction itself? Or perhaps even route the getfunction to a getmethod present on the targetobject?

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

Do keep in mind, however, this is only ever applicable in ES6. Transpilers such as Babelcannot, and I quote:

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

Due to the limitations of ES5, Proxies cannot be transpiled or polyfilled.

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

The solution presented above does however work perfectly fine as long as this condition is met. It is, for instance, a perfectly viable option in Node.js.

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

回答by Chris Morgan

No. Due to the way JavaScript works, the equivalent would be like Python's __getattr__/__getitem__, rather than PHP's __call, as it would need to be dealt with when retrieving the attribute rather than when calling it.

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

Then, you can look at a question like Python's __getattr__ in Javascriptwhich answers it in that way.

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

See also such questions as these:

另请参阅以下问题:

回答by Wildhoney

Although it's not an elegant way as we've already deduced that JavaScript does not have a __call, method_missing, __getattr__it is possible to create combinations of properties to create concrete functions that relay to a single method, passing along the properties that were used to create it.

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

One example is Myriad.js.

一个例子是Myriad.js