To build upon @amirnissim's answer a slight bit.
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.
Mimicking PHP's Magic Methods
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:
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 __get
method and __call
method at the same time. As for the __call
version, we are simply returning a function for the user to enter arguments into.
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 logic
resides:
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 MyProxy
class, we can trigger the custom logic we implemented:
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!
It would, of course, also be possible to return any other type of value from the get
function, we could just as well return a string or an integer.
Ease of Use; Usage Through Inheritance
In order to make this even easier to use, may I suggest wrapping the asProxy
method into another class, then simply extending any class that needs the "magic method" functionality with the class containing the asProxy
method? By simply returning the asProxy
method from the constructor, you are basically given the same functionality you would see in PHP, in JavaScript.
Of course, it would also be somewhat required that the get method
is 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 get
function itself? Or perhaps even route the get
function to a get
method present on the target
object?
Do keep in mind, however, this is only ever applicable in ES6. Transpilers such as Babel cannot, and I quote:
Due to the limitations of ES5, Proxies cannot be transpiled or polyfilled.
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.