JavaScript

JavaScript 継承のようなもの

More than 3 years have passed since last update.


最初に

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からは逃れられない。

書き方とかもっと良いものがあれば是非コメント下さい。