境界を越える: JavaScript の言語機能

プログラミング言語の「醜いアヒルの子」を探る

JavaScript はプログラミング言語の厄介者として、あざけりの対象となりがちです。その原因には、貧弱な開発ツールや、HTML ページ用の文書オブジェクト・モデルとして複雑で一貫性に欠けること、ブラウザーによって実装が異なることなどがあげられます。しかし JavaScript は、単なるオモチャをはるかに上回るものです。この記事では、Bruce Tate が JavaScript の言語機能を探ります。

Bruce Tate (bruce.tate@j2life.com), President, RapidRed

Bruce TateBruce Tateは父であり、マウンテンバイク乗りであり、カヤック乗りであり、そしてテキサス州オースチンに住んでいます。Joltを受賞した『Better, Faster, Lighter Java』を含め、ベストセラーとなった3冊の著書を執筆しています。最近、『Spring: A Developer's Notebook』を発刊しました。彼はIBMに13年間在籍した後、J2Life, LLC社を設立し、Java技術とRubyに基づく軽量開発戦略とアーキテクチャーを専門としたコンサルティングを行っています。



2006年 12月 19日

ほとんどの Web 開発者は、1 度か 2 度は JavaScript を呪ったことがあるはずです。この悩める言語は、DOM (document object model) と呼ばれる複雑なプログラミング・モデルの重みで沈んでおり、実装やデバッグのためのツールは貧弱であり、その上ブラウザーによって実装が異なります。最近まで、多くの開発者にとって JavaScript は、好意的に言っても必要悪であり、ひどい場合はオモチャにすぎませんでした。

しかし JavaScript は次第に重要なものになりつつあり、そして Web 開発用に最も広く利用できるスクリプト言語としての地位を保っています。一部の業界リーダー達は、JavaScript が再び頻繁に使われるようになったことから、JavaScript を改めて見直しつつあります。Ajax (Asynchronous JavaScript + XML) のようなプログラミング手法によって、Web ページはより一層対話型になります。Apache Cocoon などのような完全な Web 開発用フレームワークでは、JavaScript を Web ページの単なるスクリプト以上のものとして多用するようになっています。また JavaScript から派生した ActionScript によって、Macromedia の Flash クライアント・サイド・フレームワークが強化されています。Java™ 開発者は、JVM で実行する JavaScript 実装である Rhino によって、JavaScript をファーストクラスのスクリプト言語として使うことができます (「参考文献」を参照してください)。

私の友人であり同僚である Stuart Halloway は Ajax に最も精通したエキスパートの 1 人ですが、彼は次のように挑戦的な言い方で JavaScript の教室を始めています。つまり、「私達は 2011 年までに、最新のアプリケーションを開発するための、より良い機能を備えた言語として JavaScript を認識するようになるだろう。」そして彼は、JavaScript プログラムは多くの場合、類似の Java プログラムよりも 10 倍も濃いと主張し、それを実現する言語機能を示し始めるのです。

このシリーズについて

この、境界を越えるシリーズでは、著者である Bruce Tate が、「今日の Java プログラマーは、他の手法や言語を学ぶことから多くを得ることができる」という概念を押し進めます。プログラミングの世界の様相は、あらゆる開発プロジェクトにとって Java が最善の選択肢であることが明確だった頃から変わってきています。他のフレームワークも Java フレームワークと同じ構築形態をとりつつあり、また他の言語での概念を学ぶことによって、それを Java プログラミングに生かすこともできます。皆さんが書く Python (あるいは Ruby や Smalltalk、その他何であれ) コードによって、皆さんの Java コーディングに対する取り組みも変わる可能性があるのです。

このシリーズでは、Java 開発とは大幅に異なりながら、同時に Java 開発にも直接応用できるプログラミング概念や手法について紹介します。場合によると、そうした技術を利用するためには、それら統合する必要があるかも知れません。また場合によると、そうした概念は直接利用できるものかもしれません。他の言語やフレームワークが、開発者やフレームワーク、Java コミュニティーでの基本的な手法にまで与えうる影響に比べると、個々のツールはそれほど重要ではありません。

この記事では、JavaScript の大きな魅力の元となっている、次のような JavaScript の機能を探ります。

  • 高階関数。高階関数というのは、関数を引数にとるか、あるいは関数を返す関数です。この機能を利用すると、JavaScript プログラマーは、Java 言語では不可能な方法で関数を操作することができます。
  • 動的型付け。バインディングを遅延させられるため、JavaScript はコンパクトで柔軟です。
  • 柔軟なオブジェクト・モデル。JavaScript のオブジェクト・モデルは、Java 言語で使用される一般的なクラス・ベースのオブジェクト・モデルとは異なり、プロトタイプという、あまり一般的ではない方法を使って継承を扱います。

おそらく皆さんは、私がこれまで境界を越えるシリーズの中で説明してきた概念として、動的型付けモデルや、高階関数という形式での関数型プログラミング、そしてオープンなオブジェクト・モデルなどは理解できると思います。これまで本格的な JavaScript 開発を行ったことがない人は、こうした機能は Python や Lisp、Smalltalk、Haskell などの本格的な言語の機能であり、JavaScript のような「オモチャ」の言語には無縁のものと思っているかもしれません。そこで、本物のコード例を示しながら、上記の概念を裏付けていくことにしましょう。

はじめに

JavaScript を設定するためには、何もする必要はありません。皆さんはこの記事をブラウザーで読んでいるので、既に JavaScript を入手しています。この記事でのプログラミング例は、どれも大部分のブラウザーで動作します。私は Firefox を使うことにします。

まず、<script type='text/javascript'> タグと </script> タグの間に JavaScript がラップされて埋め込まれた単純な Web ページをロードします。リスト 1 は、Hello, World というテキストを表示します。

リスト 1. Hello, World
<script type='text/javascript'>
alert('Hello, World.')
</script>

このコードを実行するには、単純に example1.html というファイルを作成し、リスト 1 をそのファイルにコピーし、そしてそのファイルをブラウザーにロードします。(この記事で使用するすべてのサンプル HTML ファイルについては、「ダウンロード」を参照してください。) ページをリロードする度に、コードが即座に実行されることがわかるでしょう。

alert は関数呼び出しであり、パラメータとして 1 つのストリングを持ちます。図 1 は、リスト 1 のコードが「Hello, World」という言葉の入った警告ボックスをポップアップする様子を示しています。このコードが HTML の body の中にあれば (ここでは body を指定しませんでしたが、ブラウザーは不完全な形式の HTML も許容し、ページ上のすべてがデフォルトで body として扱われます)、JavaScript はページがロードされると即座に実行されます。

図 1. Hello, world
Hello, World.

実行を遅らせる必要がある場合には、HTML の <head> 要素の中で JavaScript 関数を宣言します。これをリスト 2 に示します。

リスト 2. 実行を遅らせる
<head>
    
    <script type='text/javascript'>
        function hello() {
            alert('Hello, World.')
        }
    </script>
</head>
<body>
    <button onclick="hello();">Say Hello</button>
</body>

リスト 2 のコードを HTML ファイルの中に入力し、そのファイルをブラウザーにロードし、そしてSay Hello ボタンをクリックすると、図 2 に示す結果が表示されます。

図 2. 実行を遅らせる
実行を遅らせる

高階関数

リスト 2 から、JavaScript の関数操作機能の一端がわかると思います。ここでは関数の名前を HTML の button タグに渡し、そして HTML に組み込まれたイベント・モデルを活用しています。私は JavaScript を利用して変数や配列の中に関数を保存することがよくあります。(後ほど、「オブジェクト・モデル」のセクションの中で、この方法が JavaScript のオブジェクト・モデルで頻繁に使われることを説明します。) 例えばリスト 3 を見てください。

リスト 3. 変数で関数を操作する
<head>
    
    <script type='text/javascript'>
        hot = function hot() {
            alert('Sweat.')
        }
        cold  = function cold() {
            alert('Shiver.')
        }
        
        function swap() {
            temp = hot
            hot = cold
            cold = temp    
            alert('Swapped.')
        }
    </script>
</head>
<body>
    <button onclick="hot();">Hot</button>
    <button onclick="cold();">Cold</button>
    <button onclick="swap();">Swap</button>
</body>

JavaScript では、関数はファーストクラス・オブジェクトであり、これらを自由に操作することができます。まず、hot と cold という 2 つの関数を宣言します。それぞれを変数に保存します。Hot ボタンあるいは Cold ボタンをクリックすると適切な関数が呼び出され、警告が生成されます。次に、Hot ボタンと Cold ボタンの値を交換する関数を宣言し、この関数を、図 3 の警告を表示する 3 番目のボタンに関連付けます。

図 3. 関数を操作する
関数を操作する

この例は、他の変数を扱う場合とまったく同じように関数を扱えることを示しています。C 開発者はこの概念を関数ポインターと考えますが、JavaScript での高階関数の概念は、それよりもずっと強力です。この機能によって、JavaScript プログラマーは、他の変数型を扱う場合とまったく同じように容易に、概念的にアクション、つまり関数を扱うことができるのです。

関数の引数として関数を使う、あるいは値として関数を返すことによって、この抽象化が高階関数の領域にまで高められます。リスト 4 はリスト 3 をごくわずかに変更したものですが、関数を返す高階関数を示しています。

リスト 4. 高階関数
<head>

    <script type='text/javascript'>

        function temperature() {
            return current
        }

        hot = function hot() {
            alert('Hot.')
        }

        cold  = function cold() {
            alert('Cold.')
        }

        current = hot

        function swap() {
            if(current == hot) {
              current = cold
            } else {
              current = hot
            }
        }
    </script>
</head>
<body>
    <button onclick="funct = temperature()();">Temperature</button>
    <button onclick="swap();">Swap</button>
</body>

この例は、変化する動作をユーザー・インターフェース・イベントにどう関連付けるか、という一般的な問題を解決しています。高階関数を使えば、非常に容易です。temperature という高階関数は、current の値 (hot 関数あるいは cold 関数のいずれかを持ちます) を返します。一風変わった関数呼び出し、temperature()() を見てください。最初の 1 組の括弧は、temperature 関数を呼び出すために必要です。2 番目の括弧は、temperature が返す関数を呼び出します。図 4 は、その出力を示しています。

図 4. 高階関数
高階関数

高階関数は関数型プログラミングの基礎をなすものであり、多くの人は、純粋なオブジェクト指向プログラミングよりも関数型プログラミングの方が高い抽象化レベルを示すものと信じています。しかし、JavaScript の強みは高階関数のみではありません。JavaScript の動的な型付けは、特に UI 開発には非常に適しているのです。


動的型付け

静的型付けの場合、対象とする操作で許容される値に対して、コンパイラーが引数の値や変数、戻り値などをチェックします。この利点は、コンパイラーによって追加のエラー・チェックを行えることです。また、静的型付けでは IDE などのツールに対して多くの情報が提供され、例えばコード補完が優れているといった特徴があります。しかし静的型付けには、次のようないくつかの欠点もあります。

  • 初期の段階で意図を宣言する必要があり、多くの場合、そのために柔軟性が損なわれます。例えば、Java クラスを変更するとそのクラスの型も変更され、必然的に再コンパイルが必要になります。これとは対照的に Ruby ではオープンなクラスが許されていますが、クラスを変更すると、やはりそのクラスの型が変更されます。
  • 多くの場合、同じ概念を表現するために、より多くのコードを入力する必要があります。例えば、引数に型情報を含める必要があり、関数に戻り値を含める必要があり、またすべての変数に型を含める必要があります。また、すべての変数を宣言する必要があり、型の変換は明示的に行う必要があります。
  • 類似の動的言語の開発サイクルよりも、コンパイルしてデプロイ、というサイクルが長いことが普通です。ただしこの問題は、ツールによって多少軽減される場合もあります。

静的型付けは、一般的にはミドルウェアやオペレーティング・システムを構築するための言語には有効です。UI 開発には高い生産性と柔軟性が要求されるため、多くの場合は動的型付けが好まれます。もちろん私は、危険性も十分承知しています。JavaScript を少しでも扱ったことのある Web 開発者であれば、コンパイラーが当然捉えるはずの変数名の入力ミスに頭を抱えたことがあるはずです。しかし、利点も否定できません。そのいくつかの例を示しましょう。

まず、オブジェクトを考えてみてください。リスト 5 では、新しいオブジェクトを作成し、color という、存在しない属性にアクセスしています。

リスト 5. 属性を導入する
<script type='text/javascript'>
    blank_object = new Object();
    blank_object.color = 'blue'
    alert('The color is ' + blank_object.color)
</script>

このアプリケーションをロードして実行すると、図 5 のような結果が得られます。

図 5. 属性を導入する
属性を導入する

JavaScript は、blue 属性が存在しなくてもエラーになりません。静的言語を支持する人達は、こうした例にはエラーが含まれる可能性が高すぎてゾッとしています。こうした方法を汚いと思う人もいるかもしれませんが、魅力も否定できないはずです。つまり属性を素早く導入することができます。この例を先ほどの例と組み合わせると、動作も導入できます。変数が関数を保持できることを思い出してください。つまり動的型付けと高階関数をベースにして、任意の動作をいつでもクラスに導入できるのです。

リスト 5 は、容易にリスト 6 のように書き直すことができます。

リスト 6. 動作を導入する
<script type='text/javascript'>
    blank_object = new Object();
    blank_object.color = function() { return 'blue'}
    alert('The color is ' + blank_object.color())
</script>

これを見ると、JavaScript では、(例えばデータの代わりに動作を導入するなど) 意味合いを劇的に変更しながら、そして構文をわずかに変更するだけで、概念を素早く切り替えられることが理解できると思います。ここで見られるような能力の一端 (実は大きな弱みでもあります) は、この言語の持つ偉大な順応性によるものです。実際、この言語のオブジェクト・モデルそのものが、JavaScript の順応性の高さを示す一例にすぎないのです。


オブジェクト・モデル

ここまで読み進むと、JavaScript が単なるオモチャ以上のものであることが実感として理解できてきたと思います。実際、多くの人は、JavaScript のオブジェクト・モデルを使うことによって、驚くほど高度な、そしてよく設計されたオブジェクト指向のソフトウェアを作成しています。しかし、そのオブジェクト・モデルは、特に継承に関しては、皆さんが慣れたものとは異なっています。

Java 言語はクラスを基にしています。アプリケーションを作成する際には、すべてのオブジェクトに対するテンプレートとして新しいクラスを作成します。そして new をコールしてそのテンプレートをインスタンス化し、新しいオブジェクトを作成します。一方 JavaScript では、プロトタイプを作成します。プロトタイプは、今後のすべてのオブジェクトを作成するインスタンスです。

前置きはこのくらいにして、実際に動作するコードを示しましょう。まずリスト 7 は、name という属性と speak というアクションを持つ、単純な Animal を作成します。次に、他の animal がこのベースを継承します。

リスト 7. コンストラクターを作成する
<script type='text/javascript'>        
function Animal() {
    this.name = "nobody"
    this.speak = function () {
        return "Who am I?"
    }
}

myAnimal = new Animal();
alert('The animal named ' + myAnimal.name + 
      ' says ' + myAnimal.speak());

</script>

リスト 7 から、図 6 に示す例が作成されます。

図 6. コンストラクターを作成する
コンストラクターを作成する

Java 開発者にとっては、リスト 7 のコードは予測不能でおかしなものに見えます。一方、独自のオブジェクトを作成しない大部分の JavaScript 開発者にとっては、Java のコードが予測不能でおかしなものに見えます。ここで少し冷静さを取り戻しましょう。

実は、次の 3 つのことを知らないために変に思えるのです。第 1 に、JavaScript はオブジェクトを、ネストした関数として表現します。つまり、リスト 7 の Animal の定義は有効な構文だということです。第 2 に JavaScript は、クラス・テンプレートではなくプロトタイプ、つまりオブジェクトの既存インスタンスに基づいてオブジェクトを構築します。funct() は呼び出しですが、new Animal() は Animal のプロトタイプに基づいてオブジェクトを構築します。最後に、JavaScript では、オブジェクトは関数や変数の単なる集合にすぎません。オブジェクトには何も型が関連付けられないため、この構造は自由に変更が可能です。

リスト 7 に戻りましょう。JavaScript が、Animal で指定されるプロトタイプに基づいて、新しいオブジェクト myAnimal を定義しているのがわかります。そうするとプロトタイプの中で属性と関数を使うことができ、さらには関数あるいは属性を再定義することもできます。こうした柔軟性は、こうした動作に慣れていない Java 開発者には気が狂ったように思えるかもしれませんが、これは非常に強力なモデルなのです。

もう少しレベルを上げましょう。prototype と呼ばれるインスタンス変数を使って、オブジェクトのベースを指定することができます。prototype インスタンス変数を、継承チェーンの中の親を指すように設定します。prototype を設定すると、作成されるオブジェクトは、指定されなかったすべてのものに対する属性と関数を継承します。こうすることによって、オブジェクト指向による継承の概念をシミュレートすることができます。例えばリスト 8 を見てください。

リスト 8. プロトタイプを持つ継承
<script type='text/javascript'>        

Animal = function() {
    this.name = "nobody"
    this.speak = function () {
        return "Who am I?"
    }
}
Dog = function() {
  this.speak = function() {
    return "Woof!"
  }
}
Dog.prototype = new Animal();

myAnimal = new Dog();
alert('The animal named ' + myAnimal.name + 
      ' says ' + myAnimal.speak());
      </script>

リスト 8 では、Dog というプロトタイプを作成しています。このプロトタイプを Animal のベースにしています。Dog は speak() メソッドを再定義しますが、name() には何もしません。次に、Dog のプロトタイプを Animal に設定しています。図 7 は、その結果を示しています。

図 7. プロトタイプを持つ継承
プロトタイプを持つ継承

JavaScript は、属性やメソッドに対する参照を、次のようにして解決しています。

  • JavaScript は、コンストラクターで定義されるオリジナルのプロトタイプに基づいてインスタンスを作成します。参照されるメソッドまたは属性は、生成されるオリジナル・コピーを使います。
  • 他の変数とまったく同じように、オブジェクトの中の変数を再定義します。そうすることによって、当然ながらオブジェクトを変更することになります。そのため、明示的に定義される属性あるいは関数は、オリジナルのプロトタイプの中で定義されるものよりも優先されます。
  • prototype と呼ばれるインスタンス変数を明示的に設定すると、JavaScript は未定義のインスタンス変数あるいは属性がないかどうか、このインスタンスを調べます。この参照は再帰的です。もし prototype で定義されたインスタンスが属性あるいは関数を見つけられないと、そのインスタンスはそのインスタンスの prototype を調べ、という具合に繰り返しが行われます。

では、JavaScript の継承モデルとは一体何なのでしょう。それは継承モデルの定義の仕方に依存します。継承の動作を定義できるので、継承をオーバーライドすることもできます。最終的には、そして驚いたことに、JavaScript はオブジェクト指向というよりもむしろ関数型の言語であり、ある賢明な構文とセマンティクスを使って、非常に高度な動作をシミュレートするのです。そのオブジェクト・モデルは驚くほど柔軟であり、完全に反映的で非常にオープン、そして非常に強力です。柔軟すぎると言う人もいるかもしれません。私は、用途に対して適切なツールを使えばよいのだと思います。


まとめ

JavaScript のオブジェクト・モデルは、この言語の他の機能の上に構築されており、Dojo (「参考文献」を参照してください) のような膨大なライブラリーをサポートしています。このモデルの柔軟性によって、フレームワークごとにオブジェクト・モデルを微妙に変更することができます。ある面で、こうした柔軟性は大きな弱みとも言えます。そうした変更の結果、悪夢のような相互運用性の問題が起こる可能性があります (ただし JavaScript 言語の柔軟性のおかげで、こうした問題はある程度緩和されます)。

しかし別のある面で、この柔軟性は大きな強みでもあります。Java 言語は長年、ベースとなるオブジェクト・モデルが柔軟性に欠け、拡張できないことから、次第に複雑になるという問題を抱えています。素晴らしいオープンソース・プロジェクトや新しい技術 (例えばアスペクト指向プログラミングや Spring プログラミング・フレームワーク、バイトコード強化ライブラリーなど) によって、典型的なエンタープライズ開発者が Java 言語を適切に使いこなすために学ぶべきものとして、山のようなコードが追加されています。

究極的に言えば、JavaScript の素晴らしい柔軟性によって、高位言語の持つ能力の一端を知ることができるのです。すべてのプロジェクトで、あるいは大部分のプロジェクトでは、同じような一連の妥協をする必要はないかもしれません。しかし、ハイプ (流行による興奮) や一般的な意見を越えた情報を元に JavaScript 言語の強みと弱みを知れば、いつそれを使うべきか、使わずに避けるべきかを、より的確に判断することができます。そしてもし皆さんが、あの狂ったような JavaScript Web サイトウィジェットを変更する羽目になったら、少なくとも JavaScript の強みをさまざまに試すことはできるはずです。次回の記事まで、境界を越え続けてください。


ダウンロード

内容ファイル名サイズ
Sample HTML files for this articlej-cb12196.zip3KB

参考文献

学ぶために

  • 『Java To Ruby: Things Every Manager Should Know』(2006 年、Pragmatic Bookshelf 刊) は、この記事の著者による本です。Java プログラミングから Ruby on Rails に切り替えることに意味があるのは、いつ、どういった場合なのか、そしてその方法について説明しています。
  • 『Beyond Java』(2005年、O'Reilly刊) も、この記事の著者による本です。Java 言語の台頭と停滞について、また、一部のニッチな領域で Java プラットフォームに対抗しうる技術について解説しています。
  • ScriptaculousPrototype は、Ruby on Rails を使って Ajax を強化するための 2 つの JavaScript フレームワークです。
  • 「Object Hierarchy and Inheritance in JavaScript」は、JavaScript の継承に関するリソースとして好適です。
  • Ajaxian は Ajax のポータルであり、JavaScript の復活を駆り立てる Ajax に関して、あらゆることを議論する場となっています。
  • Dojo はオープンソースの JavaScript ツールキットです。
  • Rhino は JVM 用の JavaScript エンジンです。
  • Java technology ゾーンには、Java プログラミングのあらゆる側面を網羅した記事が豊富に用意されています。
  • developerWorks のAjax Resource Center では、Ajax に関するすべてが提供されています。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology, Web development
ArticleID=219196
ArticleTitle=境界を越える: JavaScript の言語機能
publish-date=12192006