私は10年以上にわたってOOP言語でプログラミングを行ってきましたが、現在JavaScriptを学んでおり、プロトタイプベースの継承に出会ったのは初めてです。 。プロトタイプ継承を適切に使用するJavaScriptアプリケーション(またはライブラリ)のよく書かれた例は何ですか?また、プロトタイプ継承をどのように/どこで使用するかを(簡単に)説明できます。
Douglas Crockfordの JavaScript Prototypal Inheritance :に関する素敵なページがあります。
5年前、JavaScriptで Classical Inheritance を書きました。 JavaScriptはクラスフリーのプロトタイプ言語であり、従来のシステムをシミュレートするのに十分な表現力があることを示しました。それ以来、私のプログラミングスタイルは、優れたプログラマーがそうすべきように進化してきました。私はプロトタイプ主義を完全に受け入れることを学び、古典的なモデルの範囲から自分自身を解放しました。
Dean Edwardの Base.js 、 MootoolsのClass または John ResigのSimple Inheritance はJavaScriptでの方法です classical inheritance 。
前述のように、ダグラス・クロックフォードの映画は、理由についての良い説明を提供し、その方法をカバーしています。しかし、数行のJavaScriptに入れるには:
// Declaring our Animal object
var Animal = function () {
this.name = 'unknown';
this.getName = function () {
return this.name;
}
return this;
};
// Declaring our Dog object
var Dog = function () {
// A private variable here
var private = 42;
// overriding the name
this.name = "Bello";
// Implementing ".bark()"
this.bark = function () {
return 'MEOW';
}
return this;
};
// Dog extends animal
Dog.prototype = new Animal();
// -- Done declaring --
// Creating an instance of Dog.
var dog = new Dog();
// Proving our case
console.log(
"Is dog an instance of Dog? ", dog instanceof Dog, "\n",
"Is dog an instance of Animal? ", dog instanceof Animal, "\n",
dog.bark() +"\n", // Should be: "MEOW"
dog.getName() +"\n", // Should be: "Bello"
dog.private +"\n" // Should be: 'undefined'
);
ただし、このアプローチの問題は、オブジェクトを作成するたびにオブジェクトが再作成されることです。別のアプローチは、次のようにプロトタイプスタックでオブジェクトを宣言することです。
// Defining test one, prototypal
var testOne = function () {};
testOne.prototype = (function () {
var me = {}, privateVariable = 42;
me.someMethod = function () {
return privateVariable;
};
me.publicVariable = "foo bar";
me.anotherMethod = function () {
return this.publicVariable;
};
return me;
}());
// Defining test two, function
var testTwo = function() {
var me = {}, privateVariable = 42;
me.someMethod = function () {
return privateVariable;
};
me.publicVariable = "foo bar";
me.anotherMethod = function () {
return this.publicVariable;
};
return me;
};
// Proving that both techniques are functionally identical
var resultTestOne = new testOne(),
resultTestTwo = new testTwo();
console.log(
resultTestOne.someMethod(), // Should print 42
resultTestOne.publicVariable // Should print "foo bar"
);
console.log(
resultTestTwo.someMethod(), // Should print 42
resultTestTwo.publicVariable // Should print "foo bar"
);
// Performance benchmark start
var stop, start, loopCount = 1000000;
// Running testOne
start = (new Date()).getTime();
for (var i = loopCount; i>0; i--) {
new testOne();
}
stop = (new Date()).getTime();
console.log('Test one took: '+ Math.round(((stop/1000) - (start/1000))*1000) +' milliseconds');
// Running testTwo
start = (new Date()).getTime();
for (var i = loopCount; i>0; i--) {
new testTwo();
}
stop = (new Date()).getTime();
console.log('Test two took: '+ Math.round(((stop/1000) - (start/1000))*1000) +' milliseconds');
イントロスペクションに関しては、若干の欠点があります。 testOneをダンプすると、有用な情報が少なくなります。また、「testOne」のプライベートプロパティ「privateVariable」は、すべてのインスタンスで共有されています。
function Shape(x, y) {
this.x = x;
this.y = y;
}
// 1. Explicitly call base (Shape) constructor from subclass (Circle) constructor passing this as the explicit receiver
function Circle(x, y, r) {
Shape.call(this, x, y);
this.r = r;
}
// 2. Use Object.create to construct the subclass prototype object to avoid calling the base constructor
Circle.prototype = Object.create(Shape.prototype);
[〜#〜] yui [〜#〜] と、Dean EdwardのBase
ライブラリを見てみましょう。 http://dean.edwards.name/weblog/2006/03/base /
YUIの場合は、 lang module 、特に YAHOO.lang.extend メソッド。そして、いくつかのウィジェットまたはユーティリティのソースを参照し、それらがそのメソッドをどのように使用しているかを確認できます。
Microsoftの ASP.NET Ajaxライブラリ、 http://www.asp.net/ajax/ もあります。
オブジェクト指向技術を使用した高度なWebアプリケーションの作成など、多くの優れたMSDN記事もあります。
ES6 class
およびextends
ES6 class
とextends
は、以前に考えられたプロトタイプチェーン操作のための単なる構文シュガーであり、ほぼ間違いなく最も標準的なセットアップです。
最初にプロトタイプチェーンと.
プロパティ検索: https://stackoverflow.com/a/23877420/895245
次に、何が起こるかを分解してみましょう。
class C {
constructor(i) {
this.i = i
}
inc() {
return this.i + 1
}
}
class D extends C {
constructor(i) {
super(i)
}
inc2() {
return this.i + 2
}
}
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// https://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined
すべての定義済みオブジェクトを含まない簡略図:
__proto__
(C)<---------------(D) (d)
| | | |
| | | |
| |prototype |prototype |__proto__
| | | |
| | | |
| | | +---------+
| | | |
| | | |
| | v v
|__proto__ (D.prototype)
| | |
| | |
| | |__proto__
| | |
| | |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype
MixuのNode book( http://book.mixu.net/node/ch6.html )から、これは私が見つけた最も明確な例です。
私は継承よりも合成を好みます:
構成-オブジェクトの機能は、他のオブジェクトのインスタンスを含むことにより、異なるクラスの集合で構成されます。継承-オブジェクトの機能は、その独自の機能とその親クラスの機能で構成されます。継承が必要な場合は、プレーンな古いJSを使用してください
継承を実装する必要がある場合は、少なくとも別の非標準の実装/マジック関数を使用しないでください。プロトタイプのプロパティを決して定義しないというルールに従う限り、純粋なES3で継承の合理的な複製を実装する方法は次のとおりです。
function Animal(name) { this.name = name; }; Animal.prototype.move = function(meters) { console.log(this.name+" moved "+meters+"m."); }; function Snake() { Animal.apply(this, Array.prototype.slice.call(arguments)); }; Snake.prototype = new Animal(); Snake.prototype.move = function() { console.log("Slithering..."); Animal.prototype.move.call(this, 5); }; var sam = new Snake("Sammy the Python"); sam.move();
これは従来の継承とは異なりますが、標準の理解可能なJavascriptであり、人々が主に求めている機能があります:チェーン可能なコンストラクターとスーパークラスのメソッドを呼び出す機能です。
PrototypeJSのClass.createを参照することをお勧めします。
行83 @ http://prototypejs.org/assets/2009/8/31/prototype.js
私が見た最高の例は、ダグラス・クロックフォードの JavaScript:The Good Parts にあります。言語についてバランスのとれた見方を得るのに役立つことは間違いなく購入する価値があります。
Douglas Crockford はJSON形式を担当し、YahooでJavaScriptの第一人者として働いています。
Javascriptでプロトタイプベースの継承の例を追加する。
// Animal Class
function Animal (name, energy) {
this.name = name;
this.energy = energy;
}
Animal.prototype.eat = function (amount) {
console.log(this.name, "eating. Energy level: ", this.energy);
this.energy += amount;
console.log(this.name, "completed eating. Energy level: ", this.energy);
}
Animal.prototype.sleep = function (length) {
console.log(this.name, "sleeping. Energy level: ", this.energy);
this.energy -= 1;
console.log(this.name, "completed sleeping. Energy level: ", this.energy);
}
Animal.prototype.play = function (length) {
console.log(this.name, " playing. Energy level: ", this.energy);
this.energy -= length;
console.log(this.name, "completed playing. Energy level: ", this.energy);
}
// Dog Class
function Dog (name, energy, breed) {
Animal.call(this, name, energy);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function () {
console.log(this.name, "barking. Energy level: ", this.energy);
this.energy -= 1;
console.log(this.name, "done barking. Energy level: ", this.energy);
}
Dog.prototype.showBreed = function () {
console.log(this.name,"'s breed is ", this.breed);
}
// Cat Class
function Cat (name, energy, male) {
Animal.call(this, name, energy);
this.male = male;
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.meow = function () {
console.log(this.name, "meowing. Energy level: ", this.energy);
this.energy -= 1;
console.log(this.name, "done meowing. Energy level: ", this.energy);
}
Cat.prototype.showGender = function () {
if (this.male) {
console.log(this.name, "is male.");
} else {
console.log(this.name, "is female.");
}
}
// Instances
const charlie = new Dog("Charlie", 10, "Labrador");
charlie.bark();
charlie.showBreed();
const penny = new Cat("Penny", 8, false);
penny.meow();
penny.showGender();
ES6では、コンストラクターとスーパーキーワードを使用して、はるかに簡単な継承の実装を使用します。
スニペット JavaScriptプロトタイプベースの継承 ECMAScriptバージョン固有の実装があります。現在のランタイムに応じて、ES6、ES5、およびES3実装のいずれを使用するかを自動的に選択します。