2

私は毎週このミートアップに参加し、有効な javascript: 68 の方法という本について話し合っていました。

項目 36: インスタンス オブジェクトにのみインスタンスの状態を保存する で、それを説明するために次の例を作成しました。

function User() {}
User.prototype = {
    hobbies: [], // should be instance state!
    addHobby: function (x) {
        this.hobbies.push(x);
    }

};

以下のユーザーをインスタンス化します。

boy = new User();
// User {hobbies: Array[0], addHobby: function}
girl = new User();
// User {hobbies: Array[0], addHobby: function}
boy.addHobby("swimming"); 
girl.addHobby("running");
// undefined
boy.hobbies
// ["swimming", "running"]
girl.hobbies
// ["swimming", "running"]

ご覧のとおり、addHobby 関数はプロトタイプ レベルで趣味に影響を与えます。

コード全体を次のように変更すると

function User() {}
    User.prototype = {
        hobbies: [], // should be instance state!
        addHobby: function (x) {
            newArr = new Array(x);
            this.hobbies = this.hobbies.concat(newArr);
        }

    };

boy = new User();
girl = new User();

boy.addHobby("swimming"); 
girl.addHobby("running");
boy.hobbies
//["swimming"]
girl.hobbies
//["running"]

その理由が割り当てのためであることはわかっています。this.hobbies = this.hobbies.concat(newArr);がプロトタイプ レベルではなくインスタンス レベルに割り当てられる理由の完全な説明を探しています。両方のインスタンスでこの用語this.hobbiesが使用されています。

4

4 に答える 4

1

それはまさに言語が定義されている方法です。仕様から

プロダクション MemberExpression : MemberExpression [ Expression ] は次のように評価されます。

  1. baseReference を MemberExpression の評価結果とします。
  2. baseValue を GetValue(baseReference) とします。
  3. propertyNameReference を Expression の評価結果とします。
  4. propertyNameValue を GetValue(propertyNameReference) とします。
  5. CheckObjectCoercible(baseValue) を呼び出します。
  6. propertyNameString を ToString(propertyNameValue) とします。
  7. 評価されている構文生成が厳密モードのコードに含まれている場合は、strict を true にします。そうでない場合は、strict を false にします。
  8. 基本値が baseValue で参照名が propertyNameString で、厳密モード フラグが strict である Reference 型の値を返します。

その Ecma Moon 言語には、オブジェクト プロトタイプのプロパティを探すことについての言及は含まれていません。左辺値メンバー式は常に、直接関係するベース オブジェクトのプロパティを参照します。

于 2013-04-26T15:14:48.333 に答える
1

「this」を使用すると、プロトタイプに何も割り当てることはできませんが、プロトタイプから読み取ることはできます。そのthis.hobbies = x;ため、プロトタイプのプロパティではなく現在のインスタンスのプロパティ「hobbies」を設定すると、同じ名前のプロトタイプ レベルのプロパティが非表示になります (つまり、boy.hobbiesプロトタイプから配列が返されなくなります。この名前の直接プロパティ)。

concat()既存の配列への参照ではなく新しい配列を返すため、プロトタイプレベルのプロパティ「趣味」を隠しています。

次の呼び出しで、インスタンス レベルの配列「hobbies」は、以前の値と新しい値を含む新しい配列によって上書きされます。

于 2013-04-26T15:17:20.380 に答える
1

オブジェクトのプロパティの値を設定すると、オブジェクトのプロトタイプ チェーンにプロパティが存在するかどうかに関係なく、プロパティはオブジェクト自体で定義されます。

これは、仕様のセクション 8.7.2で説明されています。

4. それ以外の場合IsPropertyReference(V)
     (a)If HasPrimitiveBase(V)が false の場合、 put をbase[[Put]]の内部メソッドとし、そうでない場合は、以下に定義する特別な内部メソッドを put とします。      (b) baseを this 値として使用し、プロパティ名、値、Throwフラグを渡すput 内部メソッドを呼び出します。[[Put]]
GetReferencedName(V)WIsStrictReference(V)

この[[Put]]方法は、セクション 8.12.5で説明されています。重要なステップは次のとおりです。

6. そうでない場合は、次のように、オブジェクトOにPという名前の名前付きデータ プロパティを作成します      (a) newDesc をプロパティ ディスクリプタとする 。      (b) PnewDesc、およびThrowを引数として渡してOの内部メソッドを呼び出します。
{[[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}
[[DefineOwnProperty]]


仕様をよく見ると、継承されたプロパティがアクセサ プロパティでない場合にのみ、割り当てによってオブジェクトのプロパティが作成されることがわかります。

つまり、以下は実際にはインスタンス プロパティを作成しません。

var name = 'foo';

function User() {} 
Object.defineProperty(User.prototype, 'name', {
    'get': function() { return name;},
    'set': function(val) { name = val;}
});

var u1 = new Users();
var u2 = new Users();
u1.name = 'bar';
console.log(u2.name); // shows 'bar'
console.log(u1) // shows 'User {}' instead of 'User {name: 'bar'}'
于 2013-04-26T15:17:34.187 に答える
0

私にとって、これはプロトタイプの継承の最良の例ではありません。

私が変更したあなたの例です:

function User() {
     this.hobbies = [];
};
    User.prototype = {

        addHobby: function (x) {
            this.hobbies.push(x);
        }

    };

boy = new User();
girl = new User();

boy.addHobby("swimming"); 
girl.addHobby("running");
于 2013-04-26T15:20:35.357 に答える