Rubyでは、定義されていないメソッドを呼び出しても、呼び出されたメソッドの名前をキャプチャし、実行時にこのメソッドの処理を実行できると思います。
JavaScriptでも同じようなことができますか?
説明しているRuby機能は「method_missing」と呼ばれます http://rubylearning.com/satishtalim/Ruby_method_missing.htm 。
これは、Firefoxなどの一部のブラウザー(spider monkey JavaScriptエンジン)にのみ存在する新しい機能です。 SpiderMonkeyでは「__noSuchMethod__」と呼ばれます https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/NoSuchMethod
今後の実装の詳細については、Yehuda Katz http://yehudakatz.com/2008/08/18/method_missing-in-javascript/ からこの記事をお読みください。
method_missingは、Pythonには存在しないのと同じ理由でJavaScriptにうまく適合しません。両方の言語で、メソッドは単なる関数であり、単なる属性です。多くの場合、オブジェクトには呼び出しできないパブリック属性があります。オブジェクトのパブリックインターフェイスが100%メソッドであるRubyとは対照的です。
JavaScriptで必要なのは、メソッドかどうかに関係なく、欠落している属性へのアクセスをキャッチするフックです。 Pythonある: __ getattr __ 特殊メソッドを参照してください。
Mozillaによる __ noSuchMethod __ 提案は、それらに取り付かれた言語にさらに別の矛盾をもたらしました。
JavaScriptの将来の道は プロキシメカニズム ( ECMAscript Harmony でもある)で、Pythonのプロトコル 属性アクセスのカスタマイズ Rubyのmethod_missingよりも。
現時点ではありません。 ECMAScript Harmonyのproxies と呼ばれる提案があり、これは同様の(実際には、はるかに多くのパワフル)機能ですが、ECMAScript Harmonyはまだリリースされておらず、おそらく数年はリリースされません。
JavaScriptでmethod_missing
を使用できるようにするJavaScriptのライブラリを作成しました:https://github.com/ramadis/unmiss
ES6プロキシを使用して動作します。 ES6クラスの継承を使用した例を次に示します。ただし、デコレータを使用して同じ結果を得ることができます。
import { MethodMissingClass } from 'unmiss'
class Example extends MethodMissingClass {
methodMissing(name, ...args) {
console.log(`Method ${name} was called with arguments: ${args.join(' ')}`);
}
}
const instance = new Example;
instance.what('is', 'this');
> Method what was called with arguments: is this
いいえ、Rubyのmethod_missingフックに直接類似したメタプログラミング機能はJavaScriptにはありません。インタープリターは、呼び出しコードがキャッチできるが、アクセスされているオブジェクトによって検出できないエラーを発生させるだけです。実行時に関数を定義することについていくつかの答えがありますが、それは同じことではありません。多くのメタプログラミング、オブジェクトの特定のインスタンスの変更、関数の定義、メモ化やデコレーターなどの機能的なことを行うことができます。しかし、Rubyまたはpythonのように、不足している関数の動的なメタプログラミングはありません。
最初のオブジェクトにメソッドが存在しない場合に別のオブジェクトにフォールスルーする方法を探していたため、この質問に行きました。それはあなたが求めるものほど柔軟ではありません-たとえば、メソッドが両方から欠落している場合、それは失敗します。
私が持っている小さなライブラリでこれを行うことを考えていました。これは、extjsオブジェクトをよりテストしやすい方法で構成するのに役立ちます。私は実際に対話のためにオブジェクトを取得するために別々の呼び出しを持っていて、これは拡張型を効果的に返すことによってこれらの呼び出しをまとめる良い方法かもしれないと思いました
これを行うには2つの方法が考えられます。
プロトタイプ
これはプロトタイプを使用して行うことができます。実際のオブジェクト上にない場合は、プロトタイプまで処理が進むためです。 thisキーワードを使用するためにドロップスルーする一連の関数の場合、これは機能しないようです。明らかに、オブジェクトは他のオブジェクトが知っていることを知らないか、気にしません。
そのすべてが独自のコードであり、これとコンストラクタを使用していない場合...これは多くの理由から良いアイデアであり、次のように実行できます。
var makeHorse = function () {
var neigh = "neigh";
return {
doTheNoise: function () {
return neigh + " is all im saying"
},
setNeigh: function (newNoise) {
neigh = newNoise;
}
}
};
var createSomething = function (fallThrough) {
var constructor = function () {};
constructor.prototype = fallThrough;
var instance = new constructor();
instance.someMethod = function () {
console.log("aaaaa");
};
instance.callTheOther = function () {
var theNoise = instance.doTheNoise();
console.log(theNoise);
};
return instance;
};
var firstHorse = makeHorse();
var secondHorse = makeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
Extjsの人たちは誤って「this」を使用しただけでなく、プロトタイプと「this」を使用するという原則に基づいて、まったく変わった古典的な継承型システムを構築したため、これは私のユースケースでは機能しません。
これは実際にプロトタイプ/コンストラクターを使用したのが初めてであり、プロトタイプを設定できないだけで少し困惑しました。コンストラクターも使用する必要があります。オブジェクト(少なくともFirefoxでは)には基本的に実際のプロトタイプである__protoを呼び出す魔法のフィールドがあります。実際のプロトタイプフィールドは、構築時にしか使用されないようです。
コピー方法
このメソッドはおそらくより高価ですが、私にはよりエレガントに見え、this
を使用しているコードでも機能します(たとえば、ライブラリオブジェクトをラップするために使用できます)。また、機能/閉鎖スタイルを使用して記述されたものにも機能します。このようなもので機能することを示すために、これをコンストラクター/コンストラクターで示しました。
ここに改造があります:
//this is now a constructor
var MakeHorse = function () {
this.neigh = "neigh";
};
MakeHorse.prototype.doTheNoise = function () {
return this.neigh + " is all im saying"
};
MakeHorse.prototype.setNeigh = function (newNoise) {
this.neigh = newNoise;
};
var createSomething = function (fallThrough) {
var instance = {
someMethod : function () {
console.log("aaaaa");
},
callTheOther : function () {
//note this has had to change to directly call the fallThrough object
var theNoise = fallThrough.doTheNoise();
console.log(theNoise);
}
};
//copy stuff over but not if it already exists
for (var propertyName in fallThrough)
if (!instance.hasOwnProperty(propertyName))
instance[propertyName] = fallThrough[propertyName];
return instance;
};
var firstHorse = new MakeHorse();
var secondHorse = new MakeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
私は実際にはどこかでbind
を使用する必要があると予想していましたが、必要ないようです。
Proxy クラスを使用できます。
_var myObj = {
someAttr: 'foo'
};
var p = new Proxy(myObj, {
get: function (target, methodOrAttributeName) {
// target is the first argument passed into new Proxy, aka. target is myObj
// First give the target a chance to handle it
if (Object.keys(target).indexOf(methodOrAttributeName) !== -1) {
return target[methodOrAttributeName];
}
// If the target did not have the method/attribute return whatever we want
// Explicitly handle certain cases
if (methodOrAttributeName === 'specialPants') {
return 'trousers';
}
// return our generic method_missing function
return function () {
// Use the special "arguments" object to access a variable number arguments
return 'For show, myObj.someAttr="' + target.someAttr + '" and "'
+ methodOrAttributeName + '" called with: ['
+ Array.prototype.slice.call(arguments).join(',') + ']';
}
}
});
console.log(p.specialPants);
// outputs: trousers
console.log(p.unknownMethod('hi', 'bye', 'ok'));
// outputs:
// For show, myObj.someAttr="foo" and "unknownMethod" called with: [hi,bye,ok]
_
p
の代わりにmyObj
を使用します。
get
は、p
のすべての属性要求をインターセプトするため、注意が必要です。したがって、specialPants
は関数ではなく文字列を返すため、p.specialPants()
はエラーになります。
unknownMethod
で実際に起こっていることは、以下と同等です。
_var unk = p.unkownMethod;
unk('hi', 'bye', 'ok');
_
これは、関数がJavaScriptのオブジェクトであるため機能します。
期待する引数の数がわかっている場合は、返される関数で通常どおりに宣言できます。
例えば:
_...
get: function (target, name) {
return function(expectedArg1, expectedArg2) {
...
_