8

JavaScript パターンと呼ばれる本を読んでいますが、その人が混乱していると思う部分が 1 つあります。

男は実際に本の中で klass デザインパターンにたどり着き、そこで彼はそれを少しずつ開発しました。彼は最初に問題を提示します。

function inherit(C, P) {
C.prototype = P.prototype;
}

彼は言い​​ます:

これにより、すべてのオブジェクトが実際に同じプロトタイプを共有するため、プロトタイプチェーンのルックアップが短く高速になります。しかし、継承チェーンのどこかにある子または孫がプロトタイプを変更すると、すべての親と祖父母に影響するため、これはドローバックでもあります。

しかし、私は実際に子のプロトタイプ say() を変更しようとしましたが、それは親には影響しませんでした。実際、子はまだ親を指していて、同じ名前の独自のプロトタイプを完全に無視していました。別のメモリ位置を指しているため、これは理にかなっています. では、その男はどうしてそのようなことを言うことができますか?以下は私の主張を証明しています:

function Parent(){}

Parent.prototype.say = function () {
return 20;
};

function Child(){
}

Child.prototype.say = function () {
return 10;
};

inherit(Child, Parent);

function inherit(C, P) {
C.prototype = P.prototype;
 } 

 var parent = new Parent();
var child = new Child();


var child2 = new Child()
alert(child.say(); //20
alert(parent.say()); //20
alert(child2.say()); //20

子や孫がプロトタイプを変更することは不可能です。

これは私の 2 番目のポイントにつながります。彼は、親プロトタイプを誤って継承チェーンの下に変更する可能性の問題 (私には再現できません) の解決策は、親プロトタイプと子プロトタイプの間の直接的なリンクを断ち切ると同時に、プロトタイプ チェーンの恩恵を受けることだと言います。彼は解決策として次のことを提案しています。

function inherit(C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
}

問題は、これが他のパターンと同じ正確な値を出力することです:

function Parent(){}

Parent.prototype.say = function () {
return 20;
};

function Child(){
}

 Child.prototype.say = function () {
return 10;
};

inherit(Child, Parent);

function inherit(C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
}

var parent = new Parent();
var child = new Child();


var child2 = new Child()
alert(child.say(); //20
alert(parent.say()); //20
alert(child2.say()); //20

空の関数がどういうわけかリンクを壊すのは意味がありません。実際、子は F を指し、F は親のプロトタイプを指します。したがって、それらはすべて同じメモリ位置を指しています。これは上記で実証されており、最初の例とまったく同じ値が出力されます。この著者が何を実証しようとしているのか、また、なぜ彼が私には理解できず、再現できないという主張をするのか、私にはわかりません。

返信ありがとうございます。

4

3 に答える 3

8

最初のポイント:

この男が言おうとしているのは、インスタンスを作成した後にプロトタイプを変更すると、子と親の両方のメソッドが変更されるということです。

例えば:

function inherit(C, P) {
  C.prototype = P.prototype;
} 

function Parent(){}
function Child(){}

inherit(Child, Parent);

Parent.prototype.say = function () {
  return 20;
};

var parent = new Parent();
var child = new Child();


// will alert 20, while the method was set on the parent.
alert( child.say() );

子のコンストラクター (親と共有されている) を変更すると、同じことが起こります。

// same thing happens here, 
Child.prototype.speak = function() {
  return 40;
};

// will alert 40, while the method was set on the child
alert( parent.speak() );

そしてあなたの2番目のポイントについて:

function inherit(C, P) {
  var F = function () {};
  F.prototype = P.prototype;
  C.prototype = new F();
}

新しい継承関数は、同じオブジェクトを指していないため、実際には親のコンストラクターを子から分離しますが、親とは関係のない新しく作成された関数のプロトタイプを指しています。したがって、実際には、親のコンストラクターのローカル コピーを作成し、そのコピーの新しいインスタンスを作成します。これにより、すべてのコンストラクター メソッドとプロパティが返されます。子のコンストラクターを変更することで、親には影響しません。

function inherit(C, P) {
  var F = function () {};
  F.prototype = P.prototype;
  C.prototype = new F();
}

function Parent(){}
function Child(){}

inherit(Child, Parent);

// same thing happens here, 
Child.prototype.speak = function() {
  return 40;
};

var parent = new Parent();

// will throw an error, because speak is undefined
alert( parent.speak() );
于 2010-11-08T21:38:53.263 に答える
2

別のプロトタイプを参照することでプロトタイプ オブジェクトを変更できるという事実は、通常の JavaScript の動作です。JavaScript のプリミティブ値は不変ですが、オブジェクトと配列は不変です。簡単な例で説明します:

var person = {name: 'greg', age: 20};

>>>person.name; //prints 'greg'

>>>person.age; //prints 20

var a = person;

>>>a.name; //prints 'greg'

a.name = 'peter';

>>>a.name; //prints 'peter'

>>>person.name; //prints 'peter'

//I've changed person.name through a.name. That's why objects in JavaScript are called mutable

配列の動作は同じです。

var arr = ['first', 'second', 'third'],
    newArr = arr;

newArr.pop();

>>>newArr; //prints ['first', 'second']

>>>arr; //prints ['first', 'second']

//Frist array was also changed

文字列の数値とブール値 (プリミティブ データ型) を見てみましょう。

var str = 'hello world',

    newStr = str;

>>>str; //prints 'hello world'

>>>newStr; //prints 'hello world'

>>>newStr.toUpperCase(); //prints 'HELLO WORLD'

>>>str; //prints 'hello world'

>>newStr; //prints 'hello world'

//Numbers and booleans have similiar behavior

同じ問題がありましたが、修正しました。ほら、私はあなたのコードにコメントしました。その中のいくつかのヒントがあなたを助けるはずです:

function Parent(){}

Parent.prototype.say = function () {
return 20;
};

function Child(){
}



/**
*
* The area you should examine i've selected below.
*
*/

//Start BUG

//new say method will not affect the Parent.prototype beacuse it wasn't assigned yet
Child.prototype.say = function () {
return 10;
};

//rewrite Child.prototype and all it's methods with Parent.prototype
inherit(Child, Parent);

//End BUG



function inherit(C, P) {
C.prototype = P.prototype;
 } 

var parent = new Parent();
var child = new Child();


var child2 = new Child()
alert(child.say(); //20
alert(parent.say()); //20
alert(child2.say()); //20

ここでの問題は、Parent.prototype をコピーして変更する代わりに、新しい Child.prototype.say メソッドを作成し、その直後に Parent.prototype の割り当てを通じて Child.prototype オブジェクト全体を書き換えることです。順序を変更するだけで問題なく動作するはずです。

于 2011-06-05T09:20:38.960 に答える
1

継承後にプロトタイプを変更すると、親のプロトタイプも変更されることがわかります。

function Parent(){}

Parent.prototype.say = function () {
return 20;
};

function Child(){
}

inherit(Child, Parent);

Child.prototype.say = function () {
return 10;
};

function inherit(C, P) {
C.prototype = P.prototype;
 } 

 var parent = new Parent();
var child = new Child();


var child2 = new Child()
alert(child.say()); //10
alert(parent.say()); //10
alert(child2.say()); //10

inherit関数の変更されたバージョンを使用する場合、ChildParentプロトタイプは別々のままです。

于 2010-11-08T21:35:24.243 に答える