Last active
July 7, 2023 06:10
-
-
Save adinan-cenci/ed4f23eb719e4c2820aedc5feb91032b to your computer and use it in GitHub Desktop.
How to emulate the php __call method in JavaScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* PHP's __call magic method is pretty useful. Unfortunately | |
* there is no equivalent in JavaScript, but it can be | |
* emulated with Proxy: | |
*/ | |
magicMethodsProxy = | |
{ | |
get: function(target, prop, receiver) | |
{ | |
// Does prop exists? Is it a method? | |
if (target[prop] != undefined && typeof target[prop] == 'function') { | |
// Wrap it around a function and return it | |
return function(...args) | |
{ | |
// in order to preserve the default arguments the method may have, we pass undefined | |
// instead of an empty arguments object | |
var value = target[prop].apply(target, (args.length ? args : undefined) ); | |
// it is important to return the proxy instead of the target in order to make | |
// future calls to this method | |
return value == target ? this : value; | |
} | |
} | |
// Does prop exists? | |
if (target[prop] != undefined) { | |
return target[prop]; | |
} | |
// Falls to __call | |
return function(...args) | |
{ | |
return target.__call(prop, args, this); | |
}; | |
} | |
} | |
/*---------------------------*/ | |
class Music | |
{ | |
constructor(file) | |
{ | |
this.file = file; | |
this.currentTime = 0; | |
this.metadata = | |
{ | |
title : '', | |
artist: '', | |
lyrics: '' | |
}; | |
// return the proxy | |
return new Proxy(this, magicMethodsProxy); | |
} | |
__call(method, args, proxy) | |
{ | |
if (this.metadata[method] == undefined) { | |
throw 'Method "'+method+'" is undefined'; | |
} | |
this.metadata[method] = args[0]; | |
// again, it is important to return the proxy | |
// instead of the target | |
return proxy; | |
} | |
play(seconds = 0) | |
{ | |
this.seconds = seconds; | |
return this; | |
} | |
} | |
/*---------------------------*/ | |
var music = new Music('the-bards-song-the-hobbit.mp3'); | |
music.play(50) // an actual method | |
.title("The Bard's song: The Hobbit") // not a method | |
.artist("Blind Guardian") | |
.lyrics(`Out in the distance | |
There's so much gold | |
The treasure that I've found | |
Is more than enough | |
Far to the hill we've to go | |
Over the moutains and ...`); | |
console.log(music.seconds); // 50 | |
console.log(music.metadata.title); // The Bard's song: The Hobbit | |
music.blindGuardianSux('just kiding, it is awesome.'); // throw exception |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment