以@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 中,这是一个完全可行的选择。