web-dev-qa-db-ja.com

JavaScriptのインスタンス変数のプロトタイプでプロパティを宣言する理由

私はJavaScriptと呼ばれるこの黒い芸術に頭を悩ませようとしています-そして、私はそれについてかなり興奮していることを認めなければなりません。私は主に「easeljs」からのコード例を見てきました。そして、私は少し混乱しています。

I(と思う)prototype変数である関数またはプロパティにclassを使用することと、this.someProp「インスタンス」変数(はい、JavaScriptにはクラスがないことを理解しています。)

私が見たコードは、自分のコードのテンプレートとしてdeclareprototype変数を使用しており、これでそれらを参照しています。

コンストラクター内:

this.name = name;

次に宣言:

Object.prototype.name;

以降、

this.name = "Freddy";

これは「new」で呼び出される関数内にあるため、この場合、理解できるように、thisは現在のオブジェクトを指します。私が困惑させているのは、プロトタイプ宣言が何をしているのか、なぜインスタンス変数に使用するのかということです。


明確化:次のコードでは、半径のプロトタイプ宣言が何を達成しているのかわかりません。

(function(){
    // constructor
    function MyCircle(radius){
        this.radius = radius;
    }
    MyCircle.prototype.radius;
    this.area = function(){
        return 3.14*this.radius*this.radius;
    };
    window.MyCircle = MyCircle;
}());
25
DaveM

プロトタイプの値には、インスタンスに直接設定されたプロパティとは異なる重要な動作があります。これを試して:

// Create a constructor
function A() {}

// Add a prototype property
A.prototype.name = "Freddy";

// Create two object instances from
// the constructor
var a = new A();
var b = new A();

// Both instances have the property
// that we created on the prototype
console.log(a.name); // Freddy
console.log(b.name); // Freddy

// Now change the property on the
// prototype
A.prototype.name = "George";

// Both instances inherit the change.
// Really they are just reading the
// same property from the prototype
// rather than their own property
console.log(a.name); // George
console.log(b.name); // George

これは、プロトタイプの継承なしでは不可能です。

hasOwnPropertyメソッドを使用して、プロパティがインスタンスプロパティかプロトタイププロパティかをテストできます。

console.log(a.hasOwnProperty("name")); // false

インスタンスはprototype値をオーバーライドできます。

b.name = "Chris";
console.log(b.hasOwnProperty("name")); // true
console.log(a.name); // George
console.log(b.name); // Chris

そして、prototype値に戻ります。

delete b.name;
console.log(b.hasOwnProperty("name")); // false
console.log(b.name); // George

これは、プロトタイプ継承の強力な部分です。

他のパターンでは:

function A() {
  this.name = "George";
}

this.name変数は、新しいインスタンスごとに再度宣言されます。

メソッドをプロトタイプで宣言された関数として持つことは、理にかなっています。すべてのインスタンスで関数定義が再宣言されるのではなく、すべてのインスタンスが単一の関数を共有できます。

関数ではなく変数に関しては、インスタンスが独自の値を設定しない場合、デフォルト値にプロトタイプを使用できます。

フィドルのコード

55

プロトタイプに保存された値は、そのプロパティのデフォルト値を提供します。

その後、そのプロパティに値を書き込むと、instanceがその新しい値を取得し、プロトタイプ上にある値を隠しますが、そのまま残されます。

これで、質問に追加したコードのコンテキストで:

MyCircle.prototype.radius;

絶対に何もしません。それは何もしません-それはreadそのプロパティを試み、そして結果を破棄します。

11
Alnitak

他の回答では、プロトタイプとインスタンスプロパティの違いについて既に説明しています。

しかし、答えに追加するために、コードスニペットを分類しましょう。

(function(){                         // <------- 1
   // constructor
   function MyCircle(radius){        // <------- 2
       this.radius = radius;         // <------- 2.1
   }
   MyCircle.prototype.radius;        // <------- 3
   this.area = function(){           // <------- 4
       return 3.14*this.radius*this.radius;
   };
   window.MyCircle = MyCircle;       // <------- 5
}());
  1. 内部コードのスコープコンテナーとして機能するIIFEを作成する
  2. コンストラクターパターンを使用してMyCircleという関数を宣言します(ただし、「構築」されることはないので、誤解を招く可能性があるため、おそらく大文字を削除する必要があります)
    • 呼び出されると、呼び出されたオブジェクトにradiusインスタンスプロパティが作成されます
  3. 存在しないradius関数のMyCircle上のprototypeプロパティにアクセスしようとすると、undefinedと評価されます
  4. グローバルウィンドウオブジェクトでareaインスタンスプロパティを作成し、それに関数式を割り当てる
  5. MyCircleオブジェクトでwindowインスタンスプロパティを作成し、それにMyCircle関数を割り当てる

概要:グローバルareaオブジェクトにMyCircleおよびwindowプロパティを作成しているようで、MyCircleが呼び出されたとき追加のradiusプロパティを作成します。

sage:エリアは半径を初期化するMyCircleに依存しているため、エリアの前にMyCircleを呼び出す必要があります。

window.MyCircle(10);
window.area(); // evaluates to 314
0
linasmnew

プロトタイプをプロパティ(変数)のデフォルト値に使用できることに同意します。コンストラクター関数は、プロパティを宣言する必要はありません。条件付きで実行できます。

function Person( name, age ) {
    this.name = name;

    if ( age ) {
        this.age = age;
    }
}

Person.prototype.sayHello = function() {
    console.log( 'My name is ' + this.name + '.' );
};

Person.prototype.sayAge = function() {
    if ( this.age ) {
        console.log( 'I am ' + this.age + ' yrs old!' ); 
    } else {
        console.log( 'I do not know my age!' );
    }
};

Person.prototype.age = 0.7;

//-----------

var person = new Person( 'Lucy' );
console.log( 'person.name', person.name ); // Lucy
console.log( 'person.age', person.age );   // 0.7
person.sayAge();                           // I am 0.7 yrs old!

Lucyのageが条件付きで宣言および初期化される方法を参照してください。

0
Michael R