空想犬猫記

※当日記では、犬も猫も空想も扱っておりません。(旧・エト記)

C++ プログラマが,JavaScript を入門してみるテスト

社内で使うちょっとした GUI 付きのツールを作成しようと思っている。色々検討(脳内会議)した結果,Web ベースでやるのが一番いいんじゃないかという話になった。で,CGI で作るだけじゃ面白く無いから,JavaScript を使って動的なページを作ってみようと思い立つ。いままで JavaScript に対して及び腰だった理由としては

  1. プロトタイプベースの OO について良く理解していない
  2. 綺麗に構造化されたコードを書く方法が分からない
  3. OO 標準ライブラリ,ToolKit みたいなものが(ありすぎて)よく分からない
  4. 代替技術が出てきて,すぐ使えない知識になるのでは

などがあった。3 については,ちょっと実際に触った感覚では,prototype.js を使って後は自前で HTML と CSS を用意すれば,それなりのことができることが分かった。なので,取りあえずは prototype.js 以外は使わない方針で行く。4 については,かれこれ一年半くらい観察してきたけど,いっこうにその気配はない。ならば,ということで 1 と 2 について勉強してみることにした。

ロクに本を読まずに Native Speaker が英語を覚えるのと同じ感覚で JavaScript に触れているため,漏れ,抜け,誤解は沢山あると思います。一応手元にある本は,以下の二冊です。私の背景知識ということで。

JavaScriptビジュアル・リファレンス (Web Designer’s Handbook Series)

JavaScriptビジュアル・リファレンス (Web Designer’s Handbook Series)


Javascript and Dhtml Cookbook

Javascript and Dhtml Cookbook

で,プロトタイプベースの OO って?

いきなり他力本願ですが,まずは はてな勉強会 をご覧になって頂くのがよいかと。この勉強会の元になった id:naoya さんのブログがここから始まっている。これらはかなりクオリティ高いです。どうでもいいけど,勉強会のタイトルに 2.0 って付いているところが,懐かしい。
えー,ここまで書いて,上の記事を読み聞きし直してみたら,かなり書くことなくなったんで,次いきます。

綺麗に構造化されたコードを書く方法

JavaScript の文法は幸い,C や Java に似ているので,手続きの制御に関しては,問題ないと思われる。後はようは,クラスを定義できて,拡張できて,タタイできれば,あとは経験で何とかなるんじゃないかということで,それについてのメモを書いておく。
namespace とかアクセス制御とか,細かいことはできないけど,そんなものは勤勉な人たちのためのものなので,気にしないことにしてと…。そこまで割り切れば意外と簡単。キモだけ書いておきます。

クラスの定義

C++

class A
{
public:
    A() : x(1) { alert("initializeA"); }
    virtual ~A() {}
    virtual void func() { alert("hogeA"); }

    int x;
};

    :

    A *a = new A;
    a->func();
    delete a;

に相当するコードは

var A = Class.create();
A.prototype = {
    initialize : function() { alert("initializeA"); },
    func : function() { alert("hogeA"); },
    x : 1
};

var a = new A;
a.func();

となる。prototype というメンバは,そのオブジェクトが new でインスタンス化されたときに,メンバになる関数なり変数の参照(プロトタイプ)が入っているハッシュで,ここに要素を追加することが,メンバの定義に相当する。

クラスの継承(もどき)

継承方法は,最新の prototype version 1.4 では,上の日記にある記述方法が使えなくなっている。これは Object.prototype の汚染が問題になる場合があるためのようだ。こちらのエントリーでは,対策を提案しているが,以下の記法で継承するのが良さげ。上で定義したクラス A を継承した B というクラスを考える C++ だと以下のような感じ。

class B : public A
{
public:
    B() { alert("initializeB"); }
    virtual ~B() {}
    virtual void func() { alert("hogeB"); }
};

A *a = new B;
a->func();
delete B;

に(ほぼ)相当する JavaScript のコードは

var B = Class.create();
B.prototype = Object.extend(new A, {
    initialize : function(){ alert("initB"); },
    func    : function() { alert("hogeB"); }});

var a = new B;
a.func();

となる。ほぼ,と言ったのは,コンストラクタの挙動がちょっと違うため。Object.extend の中で new A したときに一応,親クラスのコンストラクタは呼ばれるんだけど,導出クラスにとって,親クラスのコンストラクタはクラス自身の初期化になっている。ややこしいけど説明を試みると,JavaScriptインスタンスは,インスタンスオブジェクトであると同時にクラスオブジェクトでもある,ということなんだろう(全然ややこしくなかった!)。JavaScript のクラス B は,new A で作られたインスタンスを継承して作られた新しいクラスオブジェクトになっているのである。new B したときには initialize は完全に上書きされてしまっている。
んー,てことは,JavaScript で new するたびに,prototype のコピーがどんどん作られるってことなんだろうか…。C++ で言うと,仮想関数テーブルをコピーしまくってる?‥‥それは変なきもする。

↑これはたぶんマチガイ。通常,new して作られたオブジェクトには prototype プロパティは付いて来ない。Object.extend() の引数でインスタンスをとっているのは,別の理由がありそうだ。prototype.js の中身を少し読まないと正確なことはわからなそうだが,どうやら prototype.js の独自仕様っぽい。

少々気になるところはあるが,プロトタイプベースの OO も「似たような雰囲気」で書けるらしいことが分かった。あと,Ruby の特異メソッドに相当する機能も,たんなる代入でできる。かようにむちゃくちゃ動的な言語なので,多態ももんだいない。

super クラス

super クラスの同名のメンバを参照したいときにはどうすればよいのか?それようの特別なメンバが作られていたりするんだろうか。差し当たって滅多に必要に迫られることはないと思うので放置しておく。

まとめ

JavaScript に関しては,恐らくこの程度の理解で何とかなりそうな予感。つづく。