JavaScript のクラスで継承されていても自身のメソッドを使う方法
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ 直接 A.prototype.foo はやりたくない
◆ 参照できるとは限らないし 名前変わると面倒
◆ super は常に親のプロトタイプを参照できるのでプロトタイプチェーンの一つ前が自分
◆ 自分のプロトタイプオブジェクトがわかればそこからプロパティを参照するだけ
◆ 定義時に prototype を変数に保管してそこを参照するほうが良さそう
◆ 1 箇所だけにまとまる
◆ クラス構文使わないなら 1 回は参照必須なので名前変えるときの負担は特に増えない
◆ 参照できるとは限らないし 名前変わると面倒
◆ super は常に親のプロトタイプを参照できるのでプロトタイプチェーンの一つ前が自分
◆ 自分のプロトタイプオブジェクトがわかればそこからプロパティを参照するだけ
◆ 定義時に prototype を変数に保管してそこを参照するほうが良さそう
◆ 1 箇所だけにまとまる
◆ クラス構文使わないなら 1 回は参照必須なので名前変えるときの負担は特に増えない
自身のメソッドを使いたい
クラスが継承されているとthis.foo()
は子クラスの方に定義があればそっちが使われます
そうじゃなくて絶対に自身のメソッドを使いたいときもたまにはあります
単純な方法は
class A {
constructor() {
this.foo()
A.prototype.foo.call(this)
}
foo(){
console.log(1)
}
}
class B extends A {
constructor() {
super()
this.foo()
}
foo() {
console.log(2)
}
}
new B()
// 2
// 1
// 2
A.prototype.foo.call を使うことです
ただ それだと A の名前が変わると この処理まで変えないといけないです
それに A が参照できる必要もあります
extends のところに直接クラス式を書いて
class B extends class {} {}
のようにしてる場合は難しいです
そういうことがあるのでできる限り this.constructor みたいな参照を使ってクラス名に依存しない方法を使いたいです
ですが JavaScript には PHP の self みたいな機能はありません
ググっても直接名前を書いてるこの方法しかみつかりませんでした
super を使ってがんばる
諦めようかとも思ったのですが A.prototype.foo みたいなのを何度も書いてるとやっぱり自分のクラスなのに名前を直接書きたくないと思って自分でどうにかしました方法はこちらです
function getSelfPrototype(_this, _super_ctor){
let p = _this.__proto__
while(p){
const n = p.__proto__
if(n === _super_ctor.prototype) {
return p
}
p = n
}
}
class X {
constructor(){
getSelfPrototype(this, super.constructor).foo.call(this)
}
foo(){
console.log(1)
}
}
class Y extends X {
constructor(){
super()
getSelfPrototype(this, super.constructor).foo.call(this)
}
foo(){
console.log(2)
}
}
class Z extends Y {
constructor(){
super()
getSelfPrototype(this, super.constructor).foo.call(this)
}
foo(){
console.log(3)
}
}
new Z()
// 1
// 2
// 3
this は自身のオブジェクトを指すので継承されていた場合に自分の prototype オブジェクトを知ることができません
this.constructor は最も子クラスのコンストラクタになります
this から __proto__ をたどってもどれが自分かわかりません
class A {
get name(){ return "A" }
}
みたいな名前を返すプロパティを作って各プロトタイプの name を見て行くことは考えましたが全部で name の実装を書くのが面倒ですし結局名前を見ています
name 一箇所とはいえ A という名前を内部で使いたくないです
ここで思いついたのが super を使う方法です
super は特別なキーワードでプロパティは自分の親クラスのものになります
つまり プロトタイプチェーンをたどったときに super の一つ手前が自分ということになります
自分のプロトタイプオブジェクトがわかれば そのオブジェクトらプロパティを取り出せば継承されていても自身のプロパティを取得できます
クラス構文を使わない場合
クラス構文を使わないなら楽なのに と思ったのですがfunction A() {
proto.foo.call(this)
}
const proto = Object.assign(A.prototype, {
foo(){
console.log(1)
}
})
class B extends A {
constructor(){
super()
this.foo()
}
foo(){
console.log(2)
}
}
new B()
// 1
// 2
使わないなら A.prototype を書かざるを得なくて ここで A という名前を使っています
クラス構文を使うなら書かなくていいから書いていないだけで 使わないのと同じように一度だけ使えば
class A{
constructor(){
super()
proto.foo.call(this)
}
foo(){
console.log(1)
}
}
const proto = A.prototype
とすればいいだけで大差ないです
ただ クラス内部のあちこちで使うと 名前の修正が面倒なのでこの方法みたいに一箇所でだけ prototype の参照を保持しておくのが良い方法かなと思います