27

関数本体を作成した後でも、関数本体を変更できるのでしょうか。

     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この場合、より多くの機能を拡張したい場合、どのように再利用しますか。

4

8 に答える 8

27

を使用する場合、コンストラクター内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、一種のフェイルセーフとフォールバックです。

編集:あなたの質問への回答:

インスタンスごとにオーバーライドするということは、プロパティまたは関数を特定のインスタンスにアタッチすることを意味します。たとえば、次のことができます。

i.goodbye = function() {
    return "Goodbye, cruel world!";
};

つまり、この動作はその特定のインスタンスに固有です(つまり、作成した他のインスタンスにのみ固有でiあり、他のインスタンスには固有ではありません)。

あなたが取り出す場合this、あなたは基本的に持っています:

hello = function() {
    return "hello, " + someValue;
}

これは、次のことを行うのと同じです。

window.hello = function() {
    return "hello, " + someValue;
}

したがって、この場合、helloはその関数へのグローバル参照です。これが意味するのは、helloどのオブジェクトにもアタッチされていないということです。

hellothis.hello = function() { .... };コンストラクター内にない場合は、未定義にすることができます。また、JavaScriptがオブジェクトのプロパティを解決するために使用する一般的なプロセスについても話していました。前に述べたように、それはプロトタイプチェーンを上る必要があります。

于 2012-08-29T17:11:49.237 に答える
3

Oを使用してオブジェクトのインスタンスを作成すると、新しく作成されたオブジェクトにインスタンスnew O("somename");メソッドが割り当てられます。次に、同じ名前の別のメソッドをに割り当てると、そのメソッドはインスタンスメソッドによってすでにシャドウされています。それで:Oprototype

Object.prototype.hello // undefined
       |
       O.prototype.hello // alternate function
         |
         i.hello // original function provided in constructor

JavaScriptはチェーンの最下部から始まり、名前に一致するものが見つかると停止します。したがって、で停止し、表示さi.helloれることはありませんO.prototype.hello

JavaScript(ECMAScript 5の時点)は、(私が知る限り)定義後に追加された(インスタンスまたはに追加された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

このテーマの詳細については、JavaScriptのOOPに関するbobinceのすばらしい回答を読んでください。

于 2012-08-29T17:13:08.700 に答える
1

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

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

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

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

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

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


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

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

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

于 2012-08-29T17:22:22.003 に答える
1

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

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"
于 2012-08-29T17:14:39.930 に答える
0

プロパティにアクセスすると、システムは最初にインスタンスでそれを探します。見つからない場合は、プロトタイプで検索します。これが、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();
于 2012-08-29T17:24:21.040 に答える
0

これは、オブジェクトのプロパティにアクセスするとき、JavaScript がオブジェクトのプロパティを最初にチェックしてから、そのプロトタイプに入るからです。

これは、基本クラスの機能をオーバーライドする Java の派生クラスに似ています。

理解を深めるために、プロパティの継承のコード例を確認してください

また、あなたの場合の 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
于 2012-08-29T17:17:17.953 に答える
0

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

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

于 2012-08-29T17:13:19.017 に答える