web-dev-qa-db-ja.com

JavaScriptは既存の関数本体を再定義してオーバーライドします

一度構築された関数本体を変更することはできますか?

_     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を再利用しますか。

23
peter

newを使用する場合、コンストラクター内のthisの値は、新しく作成されたオブジェクトを指します(newの動作の詳細については、 この回答 および この回答 )。したがって、新しいインスタンスiにはhello関数があります。オブジェクトのプロパティにアクセスしようとすると、それが見つかるまでプロトタイプチェーンをたどります。 helloはオブジェクトのインスタンスに存在するため、プロトタイプチェーンをたどってhelloを返すhhhhhhhhのバージョンにアクセスする必要はありません。ある意味では、インスタンスのデフォルトの実装をオーバーライドしました。

コンストラクター内でhellothisに割り当てない場合、この動作を確認できます。

_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はhelloiプロパティがあることを認識し、それを使用します。 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がオブジェクトのプロパティを解決するために使用する一般的なプロセスについても話していました。前に述べたように、それはプロトタイプチェーンを上ることです。

23
Vivin Paliath

new O("somename");を使用してOオブジェクトのインスタンスを作成すると、新しく作成されたオブジェクトにinstanceメソッドが割り当てられます。その後、同じ名前の別のメソッドをOprototypeに割り当てると、そのメソッドはインスタンスメソッドによって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の素晴らしい答え を読んでください。

3
Sean Vieira

まず、プロトタイプの継承を理解する必要があります。

Oをコンストラクターとして使用してオブジェクトを作成すると、次のようになります。

  • 最初に、新しいオブジェクトが作成されます。
  • 2番目に、そのオブジェクトのhelloプロパティが関数に設定されます(定義したコンストラクターを介して)。
  • 3番目、オブジェクトからの秘密リンク、O.prototypeオブジェクト、作成されます。

Oオブジェクトのプロパティを参照する場合、それらのプロパティは最初にオブジェクト自体で検索されます。オブジェクトがプロパティ自体を持っていない場合にのみ、そのプロトタイプを参照します。

次に、クロージャを理解する必要があります。

someValueは、O関数で定義された変数(プロパティではありません)です。同じ関数(またはO関数内で定義された関数)でも定義されている他のものからのみアクセスできます。したがって、「someValue wasclosed over」と言います。 Oの外部で定義した関数からアクセスすることはできません。


目的を達成するには、someValueをプロパティに設定する必要があります(これにより、privateのようなものではなく、publicのようなものになります)。または、someValueの元の定義内でOへのアクセスを必要とするすべての関数を定義する必要があります。

何を変更するにはi.helloiが作成された後を指します。オブジェクトのプロパティを直接設定する必要があります。

i.hello = function () { /* stuff */ };

1
MicronXD

いいえ。できませんが、別の方法で継承をパターン化することにより、この制限を回避する方法の良い例があります。

JavaScriptオーバーライドメソッド

// 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();
0
RajV

オーバーライドメソッド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);
0
SmaLL

正しく思い出せば、オブジェクトの直接メンバーである関数は、そのオブジェクトのプロトタイプの同じ名前のメンバーよりも優先されます。したがって、O.prototype.helloO.hello、前者はコードの後半で定義されていますが。

someValueを使用できない理由O.prototype.helloは、someValueのスコープがコンストラクター関数と、その中で定義または実行される関数に制約されるためです。 O.prototype.helloOコンストラクターのスコープ外で定義され、someValueについては知りません

0
jackwanders

これは、オブジェクトのプロパティにアクセスすると、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
0
Sujay