この文章は、トロント行きの飛行機(フライト11時間)の中で、書籍もなくネットが一切使えなかった為、やむを得ずjavascriptの挙動を推測しながら書いてそれをまとめたものである。参考にしたのはcloneしてあったProtoBuf.jsやByteBuffer.js等の各種ライブラリのソースコード。
よって、間違っている部分が多々あるのでjavascriptについて知りたい人は参考にしないように。他の言語経験者がjavascriptを始めたとき、どういうミスを犯すか、思考過程を知る為のサンプルにはなるかもしれない。
javascriptについては、たまにjQueryを使ったりするくらいで言語そのものをちゃんと勉強した事は無かった。そろそろjavascirptを始めようとして、勉強をしようと思った次第である。クラスベースのオブジェクト指向言語は経験済み。
javascriptにおけるオブジェクト
よくあるクラスベースのオブジェクト指向では入れ物(クラス)をを作ってから、それをnewすることでインスタンスが作られる。ギークハウス新丸子のY氏が、「たい焼き機はクラスで、たいやきはインスタンスなんだよ」という説明してくれていたっけ。
一方、javascirptにクラスは無く、プロトタイプベースのオブジェクト指向と呼ばれている。プロトタイプが何か、というのは良くわかっていない。ただ、各コードを読んでみた結果、よくあるPersonクラス相当のものを以下のように定義出来る事が分かった。
var Person = function(name, age) {
this.name = name;
this.age = age;
};
var one = new Person("Mikami", 27);
var two = new Person("Hoge", 50);
console.log(one.name);
console.log(one.age);
console.log(two.name);
console.log(two.age);
結果:
Mikami
27
Hoge
50
おお、それっぽく動作しているぞ!しかし、new Person(...)を実行したときには何が返っているのだろう?そもそも、Personにバインドされている関数は何も返していないし、この関数に何かをreturnさせてみても結果は変わらない。ここで推測したのが「newキーワード付きでPerson関数を呼び出すと、呼び出した関数を最後まで実行させたクロージャが返る」というものである。
上記が正しいかどうかを探る為、Personにバインドしているfunctionにおけるthisオブジェクトと、new実行後に作られるオブジェクトが一致しているかどうかを調べる。
調べたかったのだが、調べ方が分からない。オブジェクトid等があるならそれを取って比較したいのだが、Chromeのデバッガを使ってもそれらしき物は保持していない。だが、少なくとも、ルックアップテーブルのようなものにoneやtwoと言った変数が登録されていて、それらが別々のオブジェクトであれば参照しているポインタが別になっているはずである。
わからん。諦めよう。とりあえずこの先はさっきの仮定が正しいものとして進める。
クラス相当のものを定義する場所
(function(global) {
var Fuga = {};
...
global['Fuga'] = Fuga
})(this);
なんだこれ?この書き方をしているライブラリが幾つかある。1つ1つ解明してみよう。
thisは何を指している?
関数を定義せずにjavascriptのファイルでthisを使用した場合、それがWindowオブジェクトになることをChromeのデバッガで確認した。
さっきの書き方の意図は、どこからでも見えるWindowオブジェクトに新たにオブジェクトを追加して、どこからでも呼び出せるようにしたいのだと思う。
しかし、わざわざ関数を作ってWindowオブジェクトを渡し、それを実行するようになっている。それだと以下のように書くのと同じではないか?と思い、それを実行してみるとやはり同じ結果になった。
var Fuga = {};
...
this['Fuga'] = Fuga
名前の重複
何故このような面倒臭い書き方をしてるのだろう。同じならいちいち関数を作らなくてもいいのに、と考えた。ただ、推測するに「この空間内でのthisは単なるクロージャである為、名前が重複する心配が無い」のでは無いか。
例:
var Person = function(name, age) {
this.name = name;
this.age = age;
};
...
var Adult = {};
var Person = {};
...
this['Adult'] = Adult;
良い例がさっぱり思いつかないが、既にグローバルで定義されている変数(Person)をローカル変数として使いたいけど、どこかで既に定義されてしまっている場合。推測するに、グローバルのスコープが有効にならないのが関数の中だけである為、このような書き方が定着したのではないか。
最後に
途中だが、電池が無くなって来たので仕上げに入る。トロントまであと6時間もあるし作りたい物は全く作れていないのだが...。
ネットや本を一切使わずにプログラミング言語を学ぶというのは挙動を推測する訓練になって良い。あと単純に楽しかった。あとでネットで調べて答え合わせをすることにする。
なお、内容についてはおよそ間違っていると思うので参考にしないように(重要)。