一度構築された関数本体を変更することはできますか?
_ var O = function(someValue){
this.hello = function(){
return "hello, " + someValue;
}
}
O.prototype.hello = function(){
return "hhhhhhh";
}
var i = new O("chris");
i.hello(); // -> this still returns the old definition "hello, chris"
_
JavascriptステートメントO.prototype.hello = function(){....}
は、hello関数の動作をオーバーライドおよび再定義しません。何故ですか ?パラメータsomeValue
を再利用しようとすると、タイプエラーが発生することがわかっています。
_ // this will fail since it can't find the parameter 'someValue'
O.prototype.hello = function(){
return "aloha, " + someValue;
}
_
なぜ実行時に機能を追加できるのか疑問に思っています
_ O.prototype.newFunction = function(){
return "this is a new function";
}
i.newFunction(); // print 'this is a new function' with no problem.
_
定義された定義を変更することはできません。私は何か間違ったことをしましたか?クラス内の関数をオーバーライドして再定義するにはどうすればよいですか?そして、オブジェクトを作成するために以前に渡したパラメーターを再利用する方法はありますか?この場合、さらに機能を拡張したい場合、どのようにsomeValue
を再利用しますか。
new
を使用する場合、コンストラクター内のthis
の値は、新しく作成されたオブジェクトを指します(new
の動作の詳細については、 この回答 および この回答 )。したがって、新しいインスタンスi
にはhello
関数があります。オブジェクトのプロパティにアクセスしようとすると、それが見つかるまでプロトタイプチェーンをたどります。 hello
はオブジェクトのインスタンスに存在するため、プロトタイプチェーンをたどってhello
を返すhhhhhhhh
のバージョンにアクセスする必要はありません。ある意味では、インスタンスのデフォルトの実装をオーバーライドしました。
コンストラクター内でhello
をthis
に割り当てない場合、この動作を確認できます。
_var O = function(someValue) {
}
O.prototype.hello = function(){
return "hhhhhhh";
}
var i = new O("chris");
console.log(i.hello()); //this prints out hhhhhhh
_
あなたがしていることは逆向きです。プロトタイプは基本的に何かの「デフォルト」形式を提供し、インスタンスごとにオーバーライドできます。デフォルトのフォームは、探しているプロパティがオブジェクトで見つからない場合にのみ使用されます。つまり、JavaScriptはプロトタイプチェーンをたどり始め、探しているものに一致するプロパティを見つけることができるかどうかを確認します。それを見つけ、それを使用します。それ以外の場合は、undefined
を返します。
最初のケースで基本的に持っているものは次のとおりです。
_Object.prototype.hello (not defined; returns "undefined")
|
+----O.prototype.hello (returns "hhhhhhhh")
|
+----i.hello (returns "hello, chris")
_
したがって、_i.hello
_を実行すると、JavaScriptはhello
にi
プロパティがあることを認識し、それを使用します。 hello
プロパティを明示的に定義しなかった場合、基本的に次のようになります。
_Object.prototype.hello (not defined; returns "undefined")
|
+----O.prototype.hello (returns "hhhhhhhh")
|
+----i.hello (is "undefined", so JavaScript will walk up the chain until
it sees O.prototype.hello, which does have a defined value
it can use.)
_
これが意味するのは、プロトタイプでデフォルトの実装を提供し、それをオーバーライドできるということです(ある意味では、サブクラス化のようです)。また、できることは、インスタンスを直接変更することにより、インスタンスごとに動作を変更することです。プロトタイプ上にあるhello
のバージョンは、一種のフェイルセーフおよびフォールバックです。
EDIT:質問への回答:
インスタンスごとにオーバーライドすることは、特定のインスタンスにプロパティまたは関数をアタッチすることを意味します。たとえば、次のことができます。
_i.goodbye = function() {
return "Goodbye, cruel world!";
};
_
つまり、この動作はその特定のインスタンスに固有です(つまり、i
のみに固有であり、作成した他のインスタンスには固有ではありません)。
this
を取り出すと、基本的に次のようになります。
_hello = function() {
return "hello, " + someValue;
}
_
これは以下と同等です:
_window.hello = function() {
return "hello, " + someValue;
}
_
したがって、この場合、hello
はその関数へのグローバル参照です。これが意味することは、hello
がどのオブジェクトにもアタッチされていないことです。
コンストラクター内にthis.hello = function() { .... };
がない場合、hello
を未定義にすることができます。 JavaScriptがオブジェクトのプロパティを解決するために使用する一般的なプロセスについても話していました。前に述べたように、それはプロトタイプチェーンを上ることです。
new O("somename");
を使用してO
オブジェクトのインスタンスを作成すると、新しく作成されたオブジェクトにinstanceメソッドが割り当てられます。その後、同じ名前の別のメソッドをO
のprototype
に割り当てると、そのメソッドはインスタンスメソッドによってalready shadowedになります。そう:
Object.prototype.hello // undefined
|
O.prototype.hello // alternate function
|
i.hello // original function provided in constructor
JavaScriptは、チェーンのbottomで始まり、名前に一致するものが見つかると停止します。したがって、i.hello
で停止し、O.prototype.hello
が表示されることはありません。
JavaScript(ECMAScript 5以降)は、定義後に追加されたインスタンスメソッド(追加によってアクセスできるようなプライベート変数を実行する良い方法を(私が知っている限りでは)提供していません。 onインスタンスまたはprototype
)。クロージャーはほとんどの方法を提供しますが、クロージャー変数にアクセスできるクロージャーの外部にメソッドを追加できるようにしたい場合は、与えるget
および/またはset
メソッドを公開する必要があります。これらの新しいメソッドはクロージャー変数にアクセスします:
// Possibility #1 - marked as private member
var O = function(someValue) {
this._someValue = someValue;
};
O.prototype.hello = function() { return "hhhh"; };
var i = new O("somename");
i.hello = function() { return "aloha," + this._someValue; };
console.log(O.hello()); // hhhh
console.log(i.hello()); // aloha, somename
// Possibility #2 - factory function + closure with get and set methods
var OMaker = function(someValue) {
var realO = function() {};
realO.prototype.getSomeValue = function() { return someValue; };
realO.prototype.setSomeValue = function(newVal) { someValue = newVal; };
realO.prototype.hello = function() { return "hhhh"; };
return realO;
};
var O = OMaker("somename"),
i = new O();
i.hello = function() { return "aloha," + this.getSomeValue(); };
console.log(O.hello()); // hhhh
console.log(i.hello()); // aloha, somename
// Possibility #3 - eschew prototype inheritance and create new objects
var O = function(someValue) {
return {
getValue: function() { return someValue; },
setValue: function(newValue) { someValue = newValue; },
hello: function() { return "hhhh"; }
};
};
var i = O(); // Note the lack of the "new" keyword
i.hello = function() { return "aloha," + this.getSomeValue(); };
console.log(O.hello()); // hhhh
console.log(i.hello()); // aloha, somename
このテーマの詳細については、 OOPでのbobinceの素晴らしい答え を読んでください。
まず、プロトタイプの継承を理解する必要があります。
O
をコンストラクターとして使用してオブジェクトを作成すると、次のようになります。
O.prototype
オブジェクト、作成されます。O
オブジェクトのプロパティを参照する場合、それらのプロパティは最初にオブジェクト自体で検索されます。オブジェクトがプロパティ自体を持っていない場合にのみ、そのプロトタイプを参照します。
次に、クロージャを理解する必要があります。
someValue
は、O
関数で定義された変数(プロパティではありません)です。同じ関数(またはO
関数内で定義された関数)でも定義されている他のものからのみアクセスできます。したがって、「someValue
wasclosed over」と言います。 O
の外部で定義した関数からアクセスすることはできません。
目的を達成するには、someValueをプロパティに設定する必要があります(これにより、private
のようなものではなく、public
のようなものになります)。または、someValue
の元の定義内でO
へのアクセスを必要とするすべての関数を定義する必要があります。
何を変更するにはi.hello
はi
が作成された後を指します。オブジェクトのプロパティを直接設定する必要があります。
i.hello = function () { /* stuff */ };
いいえ。できませんが、別の方法で継承をパターン化することにより、この制限を回避する方法の良い例があります。
// Create a class
function Vehicle(color){
this.color = color;
}
// Add an instance method
Vehicle.prototype.go = function(){
return "Underway in " + this.color;
}
// Add a second class
function Car(color){
this.color = color;
}
// And declare it is a subclass of the first
Car.prototype = new Vehicle();
// Override the instance method
Car.prototype.go = function(){
return Vehicle.prototype.go.call(this) + " car"
}
// Create some instances and see the overridden behavior.
var v = new Vehicle("blue");
v.go() // "Underway in blue"
var c = new Car("red");
c.go() // "Underway in red car"
プロパティにアクセスすると、システムはまずインスタンスでそのプロパティを探します。見つからない場合は、プロトタイプで探します。これが、O.prototype.helloではなくthis.helloが使用されている理由です。
Helloの実装をオーバーライドする場合は、JavaScript継承を使用する必要があります。基本的な例を次に示します。
var A = function(){
console.log("A is getting constructed");
};
A.prototype.constructor = A;
A.prototype.someValue = 1;
A.prototype.hello = function() {
console.log("A.hello(): " + this.someValue);
};
var B = function(){
//Constructor of A is automatically called before B's
console.log("B is getting constructed");
};
B.prototype = new A; //Inherit from A
B.prototype.constructor = B;
B.prototype.hello = function() {
console.log("B.hello() called");
console.log("Calling base class method");
A.prototype.hello.call(this);
};
var a = new A();
a.hello();
var b = new B();
b.hello();
オーバーライドメソッドrefreshEditor作成されたインスタンスの場合:
var cp = hot1.getPlugin('comments');
cp.refreshEditor = (function (original) {
return function (force) {
//do something additional
if(console) {
console.log('test!!!!!!!!!');
console.log(force)
}
original.call(cp, force);
}
})(cp.refreshEditor);
正しく思い出せば、オブジェクトの直接メンバーである関数は、そのオブジェクトのプロトタイプの同じ名前のメンバーよりも優先されます。したがって、O.prototype.hello
はO.hello
、前者はコードの後半で定義されていますが。
someValue
を使用できない理由O.prototype.hello
は、someValue
のスコープがコンストラクター関数と、その中で定義または実行される関数に制約されるためです。 O.prototype.hello
はO
コンストラクターのスコープ外で定義され、someValue
については知りません
これは、オブジェクトのプロパティにアクセスすると、JavaScriptが最初にオブジェクトのプロパティをチェックしてから、プロトタイプに進むためです。
これは、基本クラスの機能をオーバーライドするJavaの派生クラスに似ています。
よりよく理解するには、 Inheriting properties のコード例を確認してください。
また、この場合のsomeValueはコンストラクター関数に対してローカルであることに注意してください。他の関数で必要な場合は、コンストラクター内でthis.someValueに割り当てる必要があります。
ここでは、次のように特定のオブジェクトのhello関数をオーバーライドできます。ただし、クラス全体ではありません。
i.hello = function(){ console.log('even here someValue is not accessible');};
var O = function(someValue){
this.someValue = someValue;
this.hello = function(){
return "hello, " + someValue;
}
}
var i = new O("chris");
console.log(i.hello()); // prints hello, chris
i.hello = function() {
return 'Hi there '+ this.someValue;
}
console.log(i.hello()); // prints Hi there chris
var test = new O('Sujay')
console.log(test.hello()) // this still prints hello, Sujay
ここではコンストラクタを変更していないため、上記の例のtest
などの他のインスタンスでは機能しないことに注意してください。
最適な方法は、次のスニペットのように、コンストラクターではなくプロトタイプでのみ関数を定義することです。
var O = function(someValue){
this.someValue = someValue;
};
O.prototype.hello = function(){
return "hello, " + this.someValue;
};
var i = new O("chris");
console.log(i.hello()); // prints hello, chris
O.prototype.hello = function() {
return 'Hi there '+ this.someValue;
}
console.log(i.hello()); // prints Hi there chris
var test = new O('Sujay')
console.log(test.hello()) // prints Hi there Sujay