JavaScriptのオブジェクト、コンストラクタ、プロトタイプ、スコープ等

Javascriptのオブジェクト、およびそれにまつわること等を、サイ本片手に覚え書き。

JavaScriptのオブジェクト

オブジェクトはプロパティの集合

Javascriptのオブジェクトは順序付けされていないプロパティの集合。プロパティには数値、文字列、関数、オブジェクトなどが格納できる。
オブジェクトリテラルは{}。

var foo = {};

上では、空のオブジェクトを作成している。

プロパティは連想配列

プロパティへのアクセスには配列演算子を使ってアクセスできる。
fooというオブジェクトがあるときに、"foo.bar"へのアクセスと"foo['bar']"へのアクセスは同じ。

var foo = {};

alert(foo.bar); //10
foo["bar"] = 200;
alert(foo.bar); //200

これを利用して、文字列を組み立ててオブジェクトにメソッド追加することもできる。
(下の例では、クロージャを使用して与えられた引数をプライベート変数vに隠蔽、あわせてアクセサメソッドgetXXXX/setXXXを追加する)

function attr_accessor(obj, key, val){
    var v = val;
    obj["get" + key] = function(){ return v; };
    obj["set" + key] = function(val){ v = val; } 
}

var foo = {};

attr_accessor(foo, "Baz", 10);
alert(foo.getBaz()); //10
foo.setBaz(100);
alert(foo.getBaz()); //100

JavaScriptの「クラス」

サイ本では、

なお、厳密には正しくないのですが、ほかの適切な言葉もないため、本書では「クラス」という言葉を使用します。
「9章 クラスとコンストラクタとプロトタイプ」

と記載されていて、「クラス」とカギ括弧付で表記するなど、この言葉の扱いに慎重。
その後ろの段落で、

JavaScriptには、このようなクラスの概念はありませんが、このクラスに近いものとしてコンストラクタとプロトタイプオブジェクトがあります。
「9章 クラスとコンストラクタとプロトタイプ」

と記載。
「クラス」という言葉を持ち出したのは、JavaC#なんかのクラスベースオブジェクト指向言語の人間に分かりやすいようにという配慮…って理解でいいのかな。
この後には、インスタンスメソッド・クラスメソッドとそれに対応するJavaScriptの実装手段が書いてあるし。

コンストラク

new演算子と一緒に使うための関数。
thisキーワードで参照するオブジェクトの初期化をおこなう。
「クラス」の定義は、コンストラクタ関数を定義するだけでよい。
なお、このコンストラクタ関数には、constructorプロパティでアクセスできる。
また、instanceof演算子はconstructorプロパティの値を調べる。
このコンストラクタのプロパティを追加すると、クラスメソッドに相当するものが実現できる。

function Bar(){ this.baz = "BAZ"; };
Bar.Hello = function(){ return "hello"; };
b = new Bar();

alert(b.baz); // BAZ
alert(b.constructor); //function Bar() { this.baz = "BAZ"; }
alert(Bar.Hello()); // hello
プロトタイプ

全てのJavaScriptのオブジェクトはプロトタイプオブジェクトの参照を含む。
プロトタイプオブジェクトのプロパティは、それを参照するオブジェクトのプロパティとして使える。
サイ本では、オブジェクトがプロトタイプからプロパティを"継承"すると表現している。
一つのプロトタイププロパティを複数のオブジェクトで継承するため、利用方法としては読み出しに限られる。
プロトタイプへのメソッド追加で、インスタンスメソッドに相当するものが実現できる。

function Bar(){};
Bar.prototype.baz = function(){ return "BAZ";};

b = new Bar();
alert(b.baz()); // BAZ

JavaScriptのスコープ

JavaScriptにおけるスコープは、実行コンテキストごとにあるスコープチェーンで制御される。

実行コンテキスト

JavaScriptは関数実行ごとに実行コンテキストを生成する。
クライアントサイドJavaScriptでは、ウィンドウまたはフレームごとに異なるグローバル実行コンテキストが定義される。

スコープチェーン

実行コンテキストごとに生成されるオブジェクトの列。

グローバルオブジェクト

JavaScritpインタプリタ起動時、コード実行前に生成されるオブジェクト。
グローバル変数の宣言は、グローバルオブジェクトのプロパティになる。
クライアントサイドJavaScriptにおいては、グローバルオブジェクトはWindowオブジェクト。

Callオブジェクト

関数が呼び出されると、Callオブジェクトという新しいオブジェクトをメソッドチェーンの先頭に追加される。
関数実行中、関数の引数とローカル変数は、このCallオブジェクトのプロパティとして格納される。

スコープチェーンと変数名探索

関数外で定義されたJavaScriptコードの場合、スコープチェーンにはグローバルオブジェクトだけがある。
関数呼び出しの場合、先頭に関数に対応するCallオブジェクト、次にグローバルオブジェクト、このふたつ。
変数名の探索は、スコープチェーンの先頭から開始され、全てのオブジェクトを探索して見つからなかったら未定義。

var x = "x";
var y = "y";
var foo = function(){
    var y = "Y";
    var z = "Z";
    function bar(){
	var z = "ZZZ";
	alert(z); //ZZZ
	alert(y); //Y
	alert(x); //x
	alert(w); // error: w is not defined
    };
    bar();
};
foo();

オブジェクトで表現する名前空間

JavaScriptには、JavaC#のような名前空間がない。
代替として、オブジェクトを使うことで名前の衝突を避けることができる。

var Foo = {};
Foo.Utility = {
    msg: function(str){ alert(str); },
    alert: function(str){ alert("#### " + str + "!!! ###"); }
};

Foo.Utility.msg("foo"); // foo
Foo.Utility.alert("foo"); // #### foo!!! ###

はてなのTen.jsも、この形。(Ten.Classとか定義されている)

おまけ:クライアントサイドJavaScript

サイ本では、大切な三つとして

  • Windowオブジェクト(クライアントJavaScriptにおけるグローバルオブジェクト/グローバル実行コンテキスト)
  • オブジェクトの階層構造とドキュメントオブジェクトモデル(DOM)
  • イベント駆動型プログラミング

とまとめてあった。

備考

Firefox3.0.3で動作確認。