JavaScriptでOOPをするための勉強の記録

まるごとJavaScript & Ajax ! Vol.1

まるごとJavaScript & Ajax ! Vol.1

この本の OOP の記事を読んで JavaScript での OOP を勉強した。
でもわかったようなわからなかったようなモヤモヤした感じだったので
わからないことを列挙してそれに対する答えを自分なりに見つける感じで勉強してみた。
記録を実験的にブログに載せてみる。

わからなかったこと

  • 2種類のオブジェクトの作成方法はどう違う?
    • var Klass = {};
    • var Klass = function(){};
  • newってなに?
    • var k = Klass;
    • var k = new Klass();
  • prototypeってなに?
    • Klass.prop = "prop";
    • Klass.prototype.prop = "prop";
  • thisってなに?
    • Java の this とはなんか違うような気がする


以下が問いに対する自分の答え。
そこらの JavaScripter なんかは二年前に通り過ぎてる話題だけど。
始めはみんな初心者なんです><

2種類のオブジェクトの作成方法はどう違う?

JavaScript の オブジェクトの作成方法には二種類あるように見える。

  • オブジェクト - {}
  • 関数オブジェクト - function(){}
var VarKlass = {};
var FuncKlass = function(){};

VarKlass.added = "added";
alert(VarKlass.added); // "added"と表示
FuncKlass.added = "added";
alert(FuncKlass.added); // "added"と表示

ここまではまったく同じに見える。


でも、オブジェクト作成時にプロパティを設定しようとすると
だんだん違いが見えてくる。

var VarKlass = { prop: "prop" };
var FuncKlass = function(){ this.prop = "prop" };

alert(VarKlass.prop); // "prop"と表示
alert(new FuncKlass().prop); // "prop"と表示

VarKlass は プロパティをハッシュで指定できるけど
FuncKlass は プロパティを処理で書かなくちゃいけない。
しかも、使うときには new を使って一度処理を実行させなきゃいけない。


こうやってみると VarKlass の書き方の方が簡潔で
きれいに見えるけど実は VarKlass の方は
別のオブジェクトを生成できない(new できない)。
絶対に1つしかそのオブジェクトを使わないなら問題はないだろうけども。

newってなに?

ひとつのオブジェクトから複数のオブジェクトを生成するときに使用する。


上で簡単に述べたように VarKlass はそもそも new できない。

var v = new VarKlass(); // VarKlass is not a constructor と表示

FuncClassの場合

var f1 = FuncKlass;
var f2 = new FuncKlass();
alert(FuncKlass.prop); // "undefined" と表示
alert(f1.prop); // "undefined" と表示
alert(f2.prop); // "prop" と表示

new を使わない f1 はただの参照。
new をすると FuncKlass が f2 にコピーされる。(正確にはコピーじゃない:後述)
JavaScript の new は Java の new よりも clone() の方が近い印象。
JavaScriptインスタンスからインスタンスを生成する
プロトタイプベースの言語であるという感じがなんとなくわかってきた。
(お菓子の)クッキーの型抜きを作ってクッキーを作る(new する)のがクラスベース OOP
分裂するクッキー(w)から新しいクッキーを作る(new する)のがプロトタイプベース OOP かな。


でも、初期化後に追加したプロパティは new でコピーされない。
この点がさきほど正確にはコピーじゃないと言った理由。

var FuncKlass = function(){ this.prop = "prop" };
FuncKlass.prop2 = "prop2";
var f = new FuncKlass();
alert(f.prop2); // undefined と表示

では、どうするかというと次で説明する prototype を使う。

prototypeって何?

VarKlass には prototype プロパティもない。

VarKlass.prototype.prot = "prot"; // VarKlass.prototype has no properties と表示


FuncKlass の場合

FuncKlass.prototype.prot = "prot";
FuncKlass.type = "type";
alert(FuncKlass.prot); // "undefined" と表示
alert(FuncKlass.type); // "type"と表示

var f = new FuncKlass();
alert(f.prot); // "prot"と表示
alert(f.type); // "undefined" と表示

prototype を使うと new した後でもつかえるプロパティを定義できる。
これを使うことによって、クラスベースでいう"クラス"を定義する。

thisってなに?

Java の感覚で this を使うとうまく行かない。
どうやら JavaScript の this は呼び出されたときの文脈によって中身がかわるようだ。

var Klass = {
    callthis: function(){
      alert(this);
    }
}
Klass.callthis(); // "Object"と表示

var g = Klass.callthis;
g(); // "Window" と表示


this を指定する apply() なんてものもある。

g.apply(Klass); // Object と表示


まとめると以下のようになる。
Java:this はそれが属するクラスへの参照
JavaScript:this は呼ばれたときのレシーバへの参照(無ければグローバルオブジェクト)
参考:fladdict.net blog: JavaScript, ActionScriptにおける .this とは何なのか?


setInterval() とか addEventListener() で指定するクロージャの中でも直感と違うので注意。

var Klass = {
  timerthis: function(){
    setInterval(function() {
      alert(this);
    }, 10000);
  }
}

Klass.timerthis(); // "Window" と表示

対策としてはクロージャの前で

var self = this;

とかやっていったん変数に代入してその変数を参照するらしい。
参考:実践 prototype.js [2] - higepon blog

まとめ

クラスベース OOPプログラマがそのままの感覚で
JavaScriptOOP しようとするとなんかよくわからない。
逆にいうとプロトタイプベース OOP の感覚がわかれば
JavaScriptOOP はそんなに難しくない(はず)。


上記のことを理解した後でもう一回本を読み直したら
上で一生懸命書いたことがちゃんと書いてあったけどな!
読解力なさすぎorz