注: ES2015 の更新については、最後を参照してください。
ES5 以前
そこにはいくつかの問題があります。
あなたのMySuperClass
関数は引数を期待していますが、それを呼び出してMyBaseClass.prototype
.
インスタンスに設定しているプロパティは、のbase
コードでは正しく機能しません。MyBaseClass
MyBaseClass
MySuperClass
MySpecificClass
これは複雑なものです。MySuperClass
3 世代 ( 、MyBaseClass
、および)を確実に行うのは非常に賢明です。MySpecificClass
これは、2 レベルの階層だけでこれを行うのは非常に簡単ですが、3 レベル以上の場合ははるかに複雑になるためです。:-)
JavaScript での継承の処理、スーパークラス メソッドの呼び出しなどについて詳しく知りたい場合は、それに関する記事を書き、それを行うためのツールキットを書きました。記事を読み、ツールキットのソース (記事を超えています) を見ると、プロトタイプ チェーンがどのように機能し、どのように動作するかを理解するのに役立つ場合があります。
これは、ツールキットを使用せず、スーパーコールを簡単にしようとしない例です。わかりやすくするためにParent
、Child
、 、 という用語をGrandChild
3 世代に使用しました。
// A parent (base) "class"
function Parent(a) {
this.a = a;
}
Parent.prototype.one = function() {
console.log("I'm Parent#one: a = " + this.a);
};
Parent.prototype.two = function() {
console.log("I'm Parent#two: a = " + this.a);
};
// A child "subclass"
function Child(a, b) {
// Chain to "superclass" constructor
Parent.call(this, a);
// Do our own init
this.b = b;
}
// Create the prototype objct that `new Child` will assign to instances
// by creating a blank object backed by `Parent.prototype`. Also set
// the `constructor` property on the object; JavaScript defines that it
// will refer back to the function on the default prototype objects, so
// we do that for consistency despite nothing in JavaScript actually
// _using_ `constructor`.
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
// Add things to `Child.prototype`
Child.prototype.one = function() {
Parent.prototype.one.call(this);
console.log("I'm Child#one: b = " + this.b);
};
Child.prototype.three = function() {
console.log("I'm Child#three: b = " + this.b);
};
// A grandchild "subclass"
function GrandChild(b, c) {
// Chain to "superclass" constructor
// Note that GrandChild has a fixed value for Parent's `a`
Child.call(this, "GrandChildFixedA", b);
// Do our own init
this.c = c;
}
// Again create a blank object to be the prototype `new GrandChild`
// assigns, again set `constructor`
GrandChild.prototype = Object.create(Child.prototype);
GrandChild.prototype.constructor = GrandChild;
// Add things to it
GrandChild.prototype.one = function() {
Child.prototype.one.call(this);
console.log("I'm GrandChild#one: c = " + this.c);
};
GrandChild.prototype.three = function() {
Child.prototype.three.call(this);
console.log("I'm GrandChild#three: c = " + this.c);
};
使用法:
var p = new Parent("ParentA");
console.log("Calling p.one");
p.one(); // "I'm Parent#one: a = ParentA"
console.log("Calling p.two");
p.two(); // "I'm Parent#two: a = ParentA"
var c = new Child("ChildA", "ChildB");
console.log("Calling c.one");
c.one(); // "I'm Parent#one: a = ChildA" then "I'm Child #one: b = ChildB"
console.log("Calling c.two");
c.two(); // "I'm Parent#two: a = ChildA"
console.log("Calling c.three");
c.three(); // "I'm Child#three: b = ChildB"
var gc = new GrandChild("GrandChildB", "GrandChildC");
console.log("Calling gc.one");
gc.one(); // "I'm Parent#one: a = GrandChildFixedA" then "I'm Child #one: b = GrandChildB" then "I'm GrandChild#one: c = GrandChildC"
console.log("Calling gc.two");
gc.two(); // "I'm Parent#two: a = GrandChildA"
console.log("Calling gc.three");
gc.three(); // "I'm Child#three: b = GrandChildB" then "I'm GrandChild#three: c = GrandChildC"
instanceof のテスト。ただし、instanceof を頻繁に使用している場合は、ダック タイピングについて読みたいと思うかもしれません。
// Some things that should be true
console.log("p instanceof Parent? " + (p instanceof Parent));
console.log("c instanceof Parent? " + (c instanceof Parent));
console.log("c instanceof Child? " + (c instanceof Child));
console.log("gc instanceof Parent? " + (gc instanceof Parent));
console.log("gc instanceof Child? " + (gc instanceof Child));
console.log("gc instanceof GrandChild? " + (gc instanceof GrandChild));
// And some things that *shouldn't* be true:
console.log("p instanceof Child? (should be false) " + (p instanceof Child));
console.log("p instanceof GrandChild? (should be false) " + (p instanceof GrandChild));
console.log("c instanceof GrandChild? (should be false) " + (c instanceof GrandChild));
ES5 が有効な環境でない場合は、この shim を次の目的で使用できますObject.create
(注:完全な shim ではなく、上記を有効にするのに十分です) 。
Object.create = function(p) {
var o;
function ctor() {
}
ctor.prototype = p;
o = new ctor();
ctor.prototype = null;
return o;
};
ツールキット スクリプトを使用すると作業が少し楽になる理由がわかります。いくつかの選択肢があります。Lineage
私のツールキットを使用して上記がどのように見えるかを次に示します。
// A parent (base) "class"
var Parent = Lineage.define(function(p) {
p.initialize = function(a) {
this.a = a;
};
p.one = function() {
console.log("I'm Parent#one: a = " + this.a);
};
p.two = function() {
console.log("I'm Parent#two: a = " + this.a);
};
});
// A child "subclass"
var Child = Lineage.define(Parent, function(p, pp) {
p.initialize = function(a, b) {
// Chain to "superclass" constructor
pp.initialize.call(this, a);
// Do our own init
this.b = b;
};
p.one = function() {
pp.one.call(this);
console.log("I'm Child#one: b = " + this.b);
};
p.three = function() {
console.log("I'm Child#three: b = " + this.b);
};
});
// A grandchild "subclass"
var GrandChild = Lineage.define(Child, function(p, pp) {
p.initialize = function(b, c) {
// Chain to "superclass" constructor
// Note that GrandChild has a fixed value for Parent's `a`
pp.initialize.call(this, "GrandChildFixedA", b);
// Do our own init
this.c = c;
};
p.one = function() {
pp.one.call(this);
console.log("I'm GrandChild#one: c = " + this.c);
};
p.three = function() {
pp.three.call(this);
console.log("I'm GrandChild#three: c = " + this.c);
};
});
使い方は同じです。
ES2015 以降
ES2015 (別名「ES6」) の時点で、JavaScript にはclass
andsuper
キーワードが追加されました。これにより、上記が劇的に単純化され、今日ではトランスパイルで使用できます。
class Parent {
constructor(a) {
this.a = a;
}
one() {
console.log("I'm Parent#one: a = " + this.a);
}
two() {
console.log("I'm Parent#two: a = " + this.a);
}
}
class Child extends Parent {
constructor(a) {
super(a);
}
one() {
super.one();
console.log("I'm Child#one: a = " + this.a);
}
three() {
console.log("I'm Child#three: a = " + this.a);
}
}
class GrandChild extends Child {
constructor(a) {
super(a);
}
one() {
super.one();
console.log("I'm GrandChild#one: a = " + this.a);
}
three() {
super.three();
console.log("I'm GrandChild#three: a = " + this.a);
}
}
// Usage
var p = new Parent("ParentA");
console.log("Calling p.one");
p.one(); // "I'm Parent#one: a = ParentA"
console.log("Calling p.two");
p.two(); // "I'm Parent#two: a = ParentA"
var c = new Child("ChildA", "ChildB");
console.log("Calling c.one");
c.one(); // "I'm Parent#one: a = ChildA" then "I'm Child #one: b = ChildB"
console.log("Calling c.two");
c.two(); // "I'm Parent#two: a = ChildA"
console.log("Calling c.three");
c.three(); // "I'm Child#three: b = ChildB"
var gc = new GrandChild("GrandChildB", "GrandChildC");
console.log("Calling gc.one");
gc.one(); // "I'm Parent#one: a = GrandChildFixedA" then "I'm Child #one: b = GrandChildB" then "I'm GrandChild#one: c = GrandChildC"
console.log("Calling gc.two");
gc.two(); // "I'm Parent#two: a = GrandChildA"
console.log("Calling gc.three");
gc.three(); // "I'm Child#three: b = GrandChildB" then "I'm GrandChild#three: c = GrandChildC"