JavaScriptはオブジェクト指向言語なようだが、スタイルや扱い方に特徴があることが、勉強していて分かってきた。いくつか重要と思われる特徴とその扱い方についてメモしておく。
1.参照(Reference)
参照は、オブジェクトへのポインター(実態が含まれている番地)を表す。この参照によるオブジェクトへのアクセスは、JavaScriptらしい柔軟なコードの書き方を可能にしている一つの要因だろう。
オブジェクトは複数のプロパティを含み、プロパティ自体がオブジェクトとなりうる。複数の変数が同じオブジェクトを参照している場合には、その内容を変えれば全ての同じ参照をしている変数(内)の値は変わる。
JavaScript
var obj = new Object(); //空のオブジェクトを生成
var objRef =obj; //オブジェクトを参照する
obj.theProperty = true; //オブジェクトにプロパティを作る
alert( obj.theProperty === objRef.theProperty); //参照変数にも同じプロパティが。
もう一つの例を。
JavaScript
var items = new Array( "あ","い","う"); //新しいArrayオブジェクトを生成
var itemsRef = items; //オブジェクトを参照する
items.push("え"); //pushはArrayオブジェクトに定義されているメソッド
alert( items.length == itemsRef.length); //lengthももとからあるプロパティ
要するに参照されている先のオブジェクトが変更されるのであって、変数自体は変わらない。
最後にこの場合はどうなるだろうか。
JavaScript
var item = "hoge"; //新しいstringオブジェクトを生成
var itemRef = item; //オブジェクトを参照
item += "baa"; //元のオブジェクトに"baa"を追加
alert( itemRef );
実は3行目の時点で新しいstringオブジェクトが生成されて”hogebaa”となりitemはそれを参照する。itemRefはもとのオブジェクト”hoge”をポイントしたままである。
従って、4行目では”hoge”が表示される。この程度なら分かるけれど、実際にコードしているときはオブジェクトが分かれることを意識しないと、バグが発見できないときがある。
2.クロージャ
JavaScriptでは、「関数の中に関数があるとき、内側の関数は外側の関数のローカル変数を保持する」。訳が分からなかった。例を挙げてみる。
JavaScript
var obj = document.getElementByID("main") // id="main"の要素を見つける
obj.style.border = "1px solid red"; // そのborderのスタイルを変更
setTimeout( function({ obj.style.display = 'none'; }, 1000); // 1秒後にそのスタイルを消すコールバック関数をセット
//アラートを表示する一般的な関数
function delayedAlert( msg, time ) {
setTimeout( function({ alert( msg); }, time );
}
delayedAlert( "hoge", 2000 );
3行目のsetTimeoutはJavaScript特有の書き方だ。通常はsetTimeout ( ‘aFunction()’, time);として別のところにaFunctionを書きたくなるはず。上記のような書き方が出来るのは、クロージャのおかげだ。つまり、setTimeoutで定義されているfunction内の変数objは1行目で取得したobjを1秒後に参照する。
後段のdelayedAlertも同じく内部の関数のmsg,timeは外側の関数のmsg, timeの参照を保持している。
このクロージャを使って、グローバルスコープから変数を隠蔽することがある。以下のようなコードは、prototype.jsでも見ることができる。クロージャについて思い及ぶまで、なぜこんな風になっているのか分からなかった。
JavaScript
(function(){ // 匿名関数でスコープを一段下げる
var msg = "sage"; // msgはグローバル変数ではない
window.onunload = function(){ // グローバルオブジェクトwindow.onloadに関数を付加
alert( msg ); // msgはクロージャによる参照
} // close function
}(); // close function and execute
3.関数のオーバーロード
JavaScriptでは関数のオーバーロードは簡単にできることではないのだが、いろんな手法によってこれを可能にしている。
関数のオーバーロードのためには(1)引数の数を特定、(2)引数の型を特定、の2つが必要である。(1)についてはarguments変数によって関数内で参照可能である。
JavaScript
function sendMessage( msg, obj ) {
if (arguments.length == 2) obj.handleMsg( msg );
else alert( msg );
}
sendMessage( "wwwwwww。");
sendMessage( "orz", {
handleMsg : function( msg ) {
alert( "やってしまった" + msg );
}
});
(2)の引数の型については、typeofステートメントを使う。
An example to be come.
4.スコープ
JavaScript
var foo = "test";
if ( true ) {
var foo = "hoge";
}
alert( foo );
function test() {
var foo = "old test";
}
test();
alert( foo );
5行目のalertではfooは”hoge”になり、最終行のalertではfooは”hoge”のままである。
JavaScriptにおける変数のスコープは関数内であり、ブロックは関係ない。ブロックスコープの言語になれた人には厄介な現象が起こるので注意が必要である。
実際にはfoo(グローバル変数)はwindowオブジェクトのプロパティになる(OperaとSafariの一部バージョンを除く)。従って、
JavaScript
var test = "test";
alert( window.test);
は、”test”になる。しかし、グローバルスコープで明示的に宣言されていない変数の場合は、関数内で使用されてもグローバル変数になるから気が抜けない。
JavaScript
function test() {
foo = "test";
}
test();
alert( window.foo );
このfooは”test”になる。
firebugのコンソールでいろいろやってみると勉強になる。








Trackback URL
3 Trackbacks/Pingbacks
[...] - rss hatena.g.hatena.ne.jp 2 users 2007年03月22日 no_tag 2 users [...]
[...] JavaScriptの特徴とその扱い方メモ | Tech de Go [...]
JavaScriptの「this」を制覇せよ…
JavaScriptでクラス(風)な実装をしていると、クラス変数の扱いに戸惑うこ……
Post a Comment