JavaScriptでRubyのmethod_missingを実装する
追記: Firefoxの実装で既に有る__noSuchMethod__に名前は合せた方が良い、というコメントを頂いたので名前を変えました。
何の役に立つか不明だけど書いてみた*1。Proxyでプロパティアクセスをフックして、存在しない場合は用意しておいた関数プロキシを返す。
上記の処理が書いてあるのはこの部分。
function enableMethodMissing(obj) { // 関数プロキシの作成 var functionHandler = createBaseHandler({}); functionHandler.get = function(receiver, name) { // プロパティアクセスの場合は何も返さない return function(){}; } var calledProperty; var trapFn = Proxy.createFunction(functionHandler, function() { // 実行の場合は obj.methodMissing を呼び出す return obj.__noSuchMethod__(calledProperty, Array.prototype.slice.call(arguments)); }); // プロパティプロキシの作成 var propertyAccessHandler = createBaseHandler(obj); propertyAccessHandler.get = function(receiver, name) { if (obj[name]) { return obj[name]; } else { // 存在しないプロパティへのアクセスは関数プロキシを返す calledProperty = name; return trapFn; } } return Proxy.create(propertyAccessHandler); // (略)
method_missing用の関数をわざわざProxy.createFunction
しているのは、プロパティアクセスの時はundefinedを返す、関数呼び出しの時はmethod_missing用の関数実行と処理を分けるため。
使うとこの様になる。
// クラス定義 // コンストラクタの中でthisをごにょごにょする。 var Ninja = function(name) { this.name = name; return enableMethodMissing(this); } // 通常のメソッド定義 Ninja.prototype.getName = function() { return this.name; } // 存在しないメソッド呼び出しがされた時の関数 Ninja.prototype.__noSuchMethod__ = function(methodName, args) { console.log('method name:' + methodName); console.log(args); }; var sasuke = new Ninja('Sasuke'); sasuke.getName(); // => Sasuke sasuke.hoge(1,2,3); // => __noSuchMethod__('hoge', [1,2,3]) sasuke.hoge; // => undefined
Proxyが何に使えるか、というのはこの動画がわかりやすかった。
*1:Firefox4以降 or JavaScriptの実験機能を有効にしたChromeでのみ動作します。