JavaScript JavaScript的プログラミング
出典: フリー教科書『ウィキブックス(Wikibooks)』
目次 |
[編集] JavaScript的プログラミング
JavaScriptは言語としての特徴に
- すべてのオブジェクトがハッシュとして管理されている
- 関数もオブジェクトである
という特徴を持っている。 この2点の特徴を組み合わせると、強力な記述能力を持つ。
[編集] 関数オブジェクト
JavaScript中では、関数もまたオブジェクトである。
つまりは
1000; // 数字型オブジェクトが作成 "Hello,JavaScript" // 文字列型オブジェクトを作成
の様にしてデータを作成する様に
function(){ 文; } // 関数オブジェクトを作成する
と書くと関数オブジェクトを作成する。 ここから、次の3文は同じ意味を持つ。
function foo(){ 文; }
var foo = function(){ 文; }
var foo = new Function( "文;" );
[編集] 関数内関数
JavaScriptでは関数もオブジェクトのため、変数と同じ様に関数内部に関数を定義することが出来る。
function foo(){
function bar(){
}
}
関数内に関数を書くことには次のメリットがある。
- 関数内関数は、外側の関数内で宣言された変数も自由に使える
- 必然的に、プログラム上近い位置に書かれるので管理しやすい
- グローバルな領域の関数と名前が衝突する心配をしなくて良い
- 関数内関数は、宣言された関数の内側でのみアクセスできるので、他のプログラムから汚される心配が無い
CやJava等の他の言語を既に習得している読者から見ると、関数内部に関数を定義できるということは驚くところもあるだろうがこれがJavaScriptの言語的特長の一つとなっている。
[編集] 使い捨て関数
JavaScriptでは、その場で定義して使い捨てる関数を作ることが出来る。以下はその具体例である。
var to = 10;
document.write( "1から" + to + "を掛けたら…" +
( function(x){
var result = 1;
for(var i = 1; i <= x; i++)
result *= i;
return result;
// 使い捨ての関数に引数を与える
})(to)
); // "1から10を掛けたら…3628800" と表示される。
このようにその場で定義して使い捨てるような関数は、一般的にラムダ式 (lambda expression) と呼ばれる。 可読性が悪いものの、使い捨てのスクリプトを書くときなどは重宝する。
この様に記述することで、次の利点がある。
- グローバルな変数を安全に宣言できる。
- 関数内で処理を分割できる。
- return文で、直接の親を脱出することから、短い処理を書くことができる。
これらのポイントは、関数の終了時にガーベージコレクション(GC)が起動して変数を回収するという点から来ている。
例えば、次の様に、複数のチェック項目を入れ子で処理をしてしまわないといけない場合があるとする。
if(/* チェック1 */){
/* 初期化処理1 */
if(/* チェック2 */){
/* 初期化処理2 */
if(/* チェック3 */){
// ここから処理
}
}
}
データをチェックし、正しい場合は、そこからデータを取り出す。 またそのデータをチェックする、という連続は、ファイルからのデータの入力、複雑な構造の配列や、XMLからのデータの取り出しなどの場合にこのような問題にあたることが多いが、これらを次の様に、インデントの無い簡潔な記述に変換可能である。
(function(){
if( true != /* チェック1 */ ) return;
/* 初期化処理1 */
if( true != /* チェック2 */) return;
/* 初期化処理2 */
if( true != /* チェック3 */) return;
// ここから処理
})();
[編集] 制御構造の相互変換
ハッシュと関数オブジェクトを組み合わせると、JavaScriptの制御構造をほぼ再現できる。 例えば、switch文は次の様にすれば、意味がほぼ同じになる。
switch(n){
case 1:
処理1;
break;
case 2:
処理2;
break;
case 3:
処理3;
break;
case 4:
処理4;
break;
}
obj = [
function(){ 処理1; },
function(){ 処理2; },
function(){ 処理3; },
function(){ 処理4; }
];
obj[n]();
ちなみに、switch文のdefault文を再現するには、try~catch文を使用して、配列内部に、要素が存在しているかをチェックすれば可能である。
var a=[
function(){ 処理1; },
function(){ 処理2; },
function(){ 処理3; },
function(){ 処理4; }
];
if( a.hasOwnProperty(n) ) a[n](); // プロパティnを持っているか調べる
else { /* default文の処理 */ }
また、if文も次のように変形できる。
if( n>5 ){
処理;
}
obj.true=function(){ 処理; }
obj[(n>5)]();
この様に、必要な制御式の多くは連想配列と関数オブジェクトの組み合わせによって再現できる。
この様に、式を関数をハッシュの組み合わせとして再現すると次のような利点がある。
- if文などの制御構造を、関数以外の方法である程度保存できるため、何度も呼び出す際に便利
- プロトタイプオブジェクト指向の利点を生かして、後々でさらに機能を継ぎ足せる
- こちらの記述の方がシンプルな場合が多くある
- 変数として扱うことから、作成・保存・消去等をある程度直感的に行うことが出来る
どちらにしろ、JavaScriptはJava(やそれに近い文法の)言語を習得している人間が理解しやすい事も目的に出来ているため、これらの記述を使うと、JavaScript初学者に読みづらくなることに注意しておく。
[編集] 余談:JavaScriptはLisp的
この章で紹介した様々な書式は、JavaScriptでは関数自体がオブジェクトであり、その代入や入れ替えを非常に簡単に行えると言う所から来ています。 この様な特徴は、C++やRubyなど他の言語でも可能ではありますが、やはり柔軟性と簡潔さに関してこれに並ぶ言語と言われればLispくらいしか見当たりません。 Lispでは、関数をラムダ記法という方法を用いて実現しておりJavaScriptとLispでは次の2文がそれぞれほぼ等価になります。
var add = function(x, y){ return x+y; }
(define add (lambda (x y) (+ x y)))
この2つの言語は、ラムダ(lambda)で定義される関数自体をオブジェクトして扱えること、引数を配列として受け取ってその数と型に特にこだわらない事の、2点のために 非常に柔軟な記述が可能になっており、そのためJavaScriptは非常にLisp的な言語と良く評価されています。