Rubyばっか書いてる人のためのJavaScriptのオブジェクト指向世界入門
まずRubyでのオブジェクト指向と言語要素に慣れている場合はJavaScriptのオブジェクト指向でかなり違和感を覚えるような気がします。
JSの中身空っぽ最小オブジェクト
var obj = {}
以下みたいな感じで、key値からvalueが参照される。ハッシュじゃんこれ。
var obj = { key: value }
JSの関数は「第1級オブジェクト」。だいたい「値」ってことと同意。function() { ... }
は別にその他の数字とか文字列とかと扱いに変わりはないないっすよという話。
var f = function() { ... }
この「関数=値」を前提にすると、JSのオブジェクトでメソッド(的なもの)を定義できることも納得する。
var obj = { method_name: function() { ... } }
なんかすごい不器用だけど、なんかかわいいな。。。
クラス プロトタイプチェーン
- JSにクラスはない
- Rubyならクラスもオブジェクトだね
じゃあどうするか。
プロトタイプチェーンを使う(ことにした。)
「別に車の設計図なんてなくても、今ある車を真似して作ればいーじゃん笑」
「クラスなんて設計図なくても、今あるオブジェクトを真似して作ればいーじゃん笑」
JS野蛮だな。。。
var objA = { name: "とつ" say: function() { alert("I love" + this.name); } } var objB = {} objB.__proto__ = objB; objC.say // 呼べちゃう
もちろんプロトタイプチェーン(Rubyとかのオブジェクト指向でいう継承)のネストを深くすることもできます。
このプロトタイプチェーン、Rubyのメソッド探索と手法は似てます。深くネストされてるときは、その継承チェーンを辿って行って一番浅いところにあったらそれを実行します。JSのプロトタイプチェーンも同じ挙動なようですね。
オブジェクトの生成
実は、上のプロトタイプチェーンの__proto__
ってやつはあんまり使われんみたいです。他のオブジェクト指向言語やってる側としては分かりやすいんだけどなー。
その代わりにJSでは、以下のようにオブジェクトを生成します。
var o = new Person('totz')
ありゃ、これはRubyでいうClassクラスのクラスメソッドであるClass.new
に似ています。違うのはJSではnewはオブジェクト生成用に定義された予約語であるということでしょうか。
よく見るオブジェクトを生成するコードの例
// コンストラクタの定義 var Person = function (name) { this.name = name; } // prototypeの拡張 Person.prototype.sayHello = function() { alert('Hello ' + this.name); } // 利用 var person = new Person('totz');
上記のコンストラクタの定義部分とprototypeの拡張部分をふくめて擬似的に「クラス定義」と呼ぶ場合が多い。コンストラクタは、Rubyでいうinitialize()ですね。
ただ、前述のスライドのようにここはクラス定義ではなくJSが本当にやっていることに基づいた方がそう書く意味も理解しやすくなりそうです。
newは以下のことをしてくれてます。
- オブジェクトの生成
- 継承
- 初期化
擬似コードで表してみると、newの挙動がかなり理解しやすくなると思う。
これが ( ^ω^) ⊃)(⊂
var o = new Person('totz')
こうじゃ ( ^ω^) ≡⊃⊂≡
var newObj = {} newObj.__proto__ = Person.prototype; // Person.prototypeの特徴をプロトタイプチェーンでnewObjに継承 Person.apply(newObj, arguments); // newObjをthisにコンストラクタ関数を実行 return newObj; // 出来上がったオブジェクトを返す
ということでprototype
は、
- プロトタイプオブジェクトの控え室
- newとコンストラクタ関数でオブジェクトを生成するときに、protoに代入される
という使われ方をするのですね。
this
ということで、どうしても理解しておきたかったJSのthisの挙動について。
なるほどこれはRubyでいうselfだな(ドヤ)となっていたらあっというまに落とし穴にはまってしまいます。
「関数はオブジェクトに束縛されない」
なるほど、メソッドじゃなくて関数なのか。。。
var obj = Person() { key: sayHello { alert('hello'); } }
これは、オブジェクトがメソッドを保持しているわけではなく、その関数はそのオブジェクトによってただ参照されているだけなのですね。 => 渡すときには、メソッドを渡しているのではなくその参照を渡しているだけ!
では、thisとは、、、
「関数コール時に、その関数が所属していたオブジェクト」
なんですね。レシーバがないときは、JSでは普通ブラウザ実装の場合 window
オブジェクトになります。Rubyのmain
オブジェクトっぽい。
つまり、thisは呼び出し時に決定されます。
参考資料
以下のスライドは他の言語のオブジェクト指向に馴染んでいる人にはまじで参考になります。
- 最強オブジェクト指向言語 JavaScript 再入門!