JavaScript のプロトタイプの継承は、非常にねじれた形をしています。私はこれをプロトタイプ継承のコンストラクター パターンと呼んでいます。原型継承には別のパターンもあります - 原型継承の原型パターンです。後者について先に説明します。
JavaScript では、オブジェクトはオブジェクトから継承されます。授業は必要ありません。これは良いことです。それは生活を楽にします。たとえば、線のクラスがあるとします。
class Line {
int x1, y1, x2, y2;
public:
Line(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
int length() {
int dx = x2 - x1;
int dy = y2 - y1;
return sqrt(dx * dx + dy * dy);
}
}
はい、これは C++ です。クラスを作成したので、オブジェクトを作成します。
Line line1(0, 0, 0, 100);
Line line2(0, 100, 100, 100);
Line line3(100, 100, 100, 0);
Line line4(100, 0, 0, 0);
これらの 4 本の線は正方形を形成します。
JavaScript にはクラスがありません。プロトタイプの継承があります。プロトタイプ パターンを使用して同じことを行いたい場合は、次のようにします。
var line = {
create: function (x1, y1, x2, y2) {
var line = Object.create(this);
line.x1 = x1;
line.y1 = y1;
line.x2 = x2;
line.y2 = y2;
return line;
},
length: function () {
var dx = this.x2 - this.x1;
var dy = this.y2 - this.y1;
return Math.sqrt(dx * dx + dy * dy);
}
};
line
次に、次のようにオブジェクトのインスタンスを作成します。
var line1 = line.create(0, 0, 0, 100);
var line2 = line.create(0, 100, 100, 100);
var line3 = line.create(100, 100, 100, 0);
var line4 = line.create(100, 0, 0, 0);
それだけです。prototype
プロパティでコンストラクター関数を混乱させることはありません。継承に必要な唯一の関数はObject.create
. この関数はオブジェクト (プロトタイプ) を受け取り、プロトタイプから継承する別のオブジェクトを返します。
残念ながら、Lua とは異なり、JavaScript はプロトタイプ継承のコンストラクター パターンをサポートしているため、プロトタイプ継承を理解するのがより難しくなっています。コンストラクター パターンは、プロトタイプ パターンの逆です。
- プロトタイプ パターンでは、オブジェクトが最も重要視されます。したがって、オブジェクトが他のオブジェクトから継承されていることが簡単にわかります。
- コンストラクター パターンでは、関数が最も重要視されます。したがって、人々はコンストラクターが他のコンストラクターから継承すると考える傾向があります。これは間違っています。
上記のプログラムは、コンストラクター パターンを使用して記述すると、次のようになります。
function Line(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
Line.prototype.length = function () {
var dx = this.x2 - this.x1;
var dy = this.y2 - this.y1;
return Math.sqrt(dx * dx + dy * dy);
};
Line.prototype
次のようにインスタンスを作成できるようになりました。
var line1 = new Line(0, 0, 0, 100);
var line2 = new Line(0, 100, 100, 100);
var line3 = new Line(100, 100, 100, 0);
var line4 = new Line(100, 0, 0, 0);
コンストラクター パターンとプロトタイプ パターンの類似点に注目してください。
create
プロトタイプのパターンでは、メソッドを持つオブジェクトを作成するだけです。コンストラクター パターンで関数を作成すると、JavaScript が自動的にprototype
オブジェクトを作成します。
- プロトタイプ パターンには、 と の 2 つのメソッドが
create
ありlength
ます。コンストラクター パターンにも、 と の 2 つのメソッドがconstructor
ありlength
ます。
prototype
コンストラクター パターンはプロトタイプ パターンの逆です。これは、関数を作成すると JavaScriptが関数のオブジェクトを自動的に作成するためです。オブジェクトには、関数自体を指すprototype
というプロパティがあります。constructor
エリックが言ったように、console.log
出力することがわかっている理由は、次のFoo
ように渡すときFoo.prototype
ですconsole.log
。
Foo.prototype.constructor
どれが自分自身であるかを見つけFoo
ます。
- JavaScript のすべての名前付き関数には、 というプロパティがあります
name
。
- したがって
Foo.name
です"Foo"
。したがって、 で文字列が見つかり"Foo"
ますFoo.prototype.constructor.name
。
編集:わかりました。JavaScript でprototype.constructor
プロパティを再定義する際に問題があることを理解しています。問題を理解するために、まずnew
演算子がどのように機能するかを理解しましょう。
- まず、上で示した図をよく見てほしい。
- 上の図には、コンストラクター関数、プロトタイプ オブジェクト、およびインスタンスがあります。
new
コンストラクタ JS が新しいオブジェクトを作成する前に、キーワードを使用してインスタンスを作成する場合。
- この新しいオブジェクトの内部
[[proto]]
プロパティは、オブジェクトの作成時にconstructor.prototype
指定されたものを指すように設定されます。
これは何を意味しますか?次のプログラムを検討してください。
function Foo() {}
function Bar() {}
var foo = new Foo;
Foo.prototype = Bar.prototype;
var bar = new Foo;
alert(foo.constructor.name); // Foo
alert(bar.constructor.name); // Bar
ここで出力を参照してください: http://jsfiddle.net/z6b8w/
- インスタンス
foo
は から継承しFoo.prototype
ます。
- したがって
foo.constructor.name
、 が表示されます"Foo"
。
- 次に、に設定
Foo.prototype
しBar.prototype
ます。
- したがって、 によって作成されましたが、
bar
から継承されます。Bar.prototype
new Foo
bar.constructor.name
です"Bar"
。_
あなたが提供したJSフィドルFoo
で、関数を作成してから次のように設定Foo.prototype.constructor
しましたfunction Bar() {}
:
function Foo() {}
Foo.prototype.constructor = function Bar() {};
var f = new Foo;
console.log(f.hasOwnProperty("constructor"));
console.log(f.constructor);
console.log(f);
Foo.prototype
のすべてのインスタンスのプロパティを変更したため、Foo.prototype
この変更が反映されます。したがってf.constructor
ですfunction Bar() {}
。f.constructor.name
そうではあり"Bar"
ません"Foo"
。
自分の目で確かめてください -f.constructor.name
です"Bar"
。
Chrome は、そのような奇妙なことを行うことで知られています。理解しておくべき重要なことは、Chrome はデバッグ ユーティリティであり、console.log
主にデバッグ目的で使用されるということです。
したがって、新しいインスタンスを作成すると、Chrome はおそらく元のコンストラクターを によってアクセスされる内部プロパティに記録しますconsole.log
。したがってFoo
、 ではなくを表示しますBar
。
これは実際の JavaScript の動作ではありません。プロパティを上書きするときの仕様によるとprototype.constructor
、インスタンスと元のコンストラクターの間にリンクはありません。
他の JavaScript 実装 (Opera コンソール、node.js、RingoJS など) は正しいことを行い、Bar
. したがって、Chrome の動作は非標準であり、ブラウザ固有のものであるため、慌てる必要はありません。
理解することが重要なのは、オブジェクトのプロパティのFoo
代わりにChrome が表示されても、他の実装と同じであることです。Bar
constructor
function Bar() {}