最初に
pythonやrubyといった他の言語は割とわかりやすいですが、JSはprototypeがベースだから結構 異なる気がする。
たぶんJavaScriptにおける継承のパターン4種類の概要と対比とか参考にされていると思いますが、スコープ(っぽいもの)がわかりづらくあまり好みの書き方ではありません。 自分の好みの書き方で継承っぽいものをしてみます。
※ECMAScript 5です。 6からはclass,extendsを使って下さい。(class実装するの遅くない?)
結論
とりあえず、最初に結論を…
var F1 = function(){
this.a = "a"
F1.prototype.print = function(){
console.log(this.a);
}
}
var F2 = function(){
this.b = "b";
F2.prototype.printa = function(){
console.log(this.a);
}
F2.prototype.printb = function(){
console.log(this.b);
}
}
F2.prototype = new F1();//スコープ(っぽい)物の外で行う。(重要)
//main
var f2 = new F2();
f2.printa() // => "a"
f2.printb() // => "b"
f2.print() // "a"
F2.prototype = new F1
関数と変数両方を自分のprototypeへ追加できます。
検証みたいなの
環境
chrome 49.0.2623.110 m
firefox 45.0.1 + firebug
パターン1 初っ端に追加
var F1 = function(){
this.a = "a"
F1.prototype.print = function(){
console.log(this.a);
}
}
var F2 = function(){
F2.prototype = new F1();//スコープ(っぽい)物の中で行う。
this.b = "b";
console.log(this.a) // => undefined
F2.prototype.printa = function(){
console.log(this.a);
}
F2.prototype.printb = function(){
console.log(this.b);
}
}
//main
var f2 = new F2();
f2.printa() // Uncaught TypeError: f2.printa is not a function
f2.printb() // 同じく
f2.print() // 同じく
ダメ。
f2 {b: "b"}
_proto_:Object
constructor:F2()
_proto_:Object
勿論、プロパティにF1のプロパティはない。
パターン2 少し後に追加
var F1 = function(){
this.a = "a"
F1.prototype.print = function(){
console.log(this.a);
}
}
var F2 = function(){
this.b = "b";
console.log(this.a) // => undefined
F2.prototype = new F1();
F2.prototype.printa = function(){
console.log(this.a);
}
F2.prototype.printb = function(){
console.log(this.b);
}
}
//main
var f2 = new F2();
f2.printa() // Uncaught TypeError: f2.printa is not a function
f2.printb() // 同上
f2.print() // 同上
パターン1と同じく ダメ。
f2のプロパティも同じ。
パターン3 2つの関数の間
var F1 = function(){
this.a = "a"
F1.prototype.print = function(){
console.log(this.a);
}
}
var F2 = function(){
this.b = "b";
console.log(this.a) // => undefined
F2.prototype.printa = function(){
console.log(this.a);
}
F2.prototype = new F1();
F2.prototype.printb = function(){
console.log(this.b);
}
}
//main
var f2 = new F2();
f2.prototype // undefined
f2.printb() // Uncaught TypeError: f2.printb is not a function
f2.print() // 同上
f2プロパティへprinta
は登録されてるみたい。
f2を覗いてみる
f2 {b: "b"}
b:"b"
__proto__:Object
constructor:function()
printa:function()
arguments:null
caller:null
length:0
name:""
prototype:Object
__proto__:function()
<function scope>
__proto__:Object
F1はない。
パターン4 一番最後
var F1 = function(){
this.a = "a"
F1.prototype.print = function(){
console.log(this.a);
}
}
var F2 = function(){
this.b = "b";
console.log(this.a) // => undefined
F2.prototype.printa = function(){
console.log(this.a);
}
F2.prototype.printb = function(){
console.log(this.b);
}
F2.prototype = new F1();//最後
}
//main
var f2 = new F2();
f2.printa() // undefined
f2.printb() // b
f2.print() // Uncaught TypeError: f2.print is not a function
printb
は動いた。
prototypeにF1はないから printa
のaはundefinedとなっている。
f2のプロパティを見る。
F2 {b: "b"}
b:"b"
__proto__:Object
constructor:function()
printa:function()
arguments:null
caller:null
length:0
name:""
prototype:Object
__proto__:function()
<function scope>
printb:function()
arguments:null
caller:null
length:0
name:""
prototype:Object
__proto__:function()
<function scope>
__proto__:Object
やはりF1はない。
パターン5 スコープ外へ
結論にあるコードのf2プロパティ
F2 {b: "b"}b: "b"
__proto__: F1
a:"a"
printa:function()
//中はパターン4と同じ
printb:function()
//中はパターン4と同じ
__proto__:Object
ちゃんとF1が追加されている。
検証まとめ
パターン1~4はF2の関数を定義している間にそのprototypeに違う関数のprototypeを入れようとするが、そもそも入れるための器が無かった。
(newした時の__proto__
はnewされる関数のprototypeを参照します。ソースはMDN)
F2自体を定義するために、スコープの外でprototypeに追加する必要がある。(当たり前と言えば当たり前)
最後に
ECMAScript 6を使うと幸せになる。(はず)
しかし、prototypeからは逃れられない。
書き方とかもっと良いものがあれば是非コメント下さい。