« マッシュアップアワードで優秀賞 | メイン | 第3回atWorks(9/8)[再告知] »

Java環境で学ぶJavaScript from 稚北講義

 稚北の講義をしてきました。第2回目は「Webプレゼンテーション層のスクリプティング」ということで、Project PhobosとSarugau JSの話をしてきました。頭の部分でJavaエンジニアのためのJavaScript講座をしたので、そこを取り出してみました。

Java環境で学ぶJavaScript
 まず、JavaSE6 (Mustang)のβ版をインストールします。現時点では、こちらからJDK 6 Beta 2を取得可能。インストール後、 JAVA_HOMEを設定、binにパスを通してjrunscriptと入力すれば対話シェルが起動します。
 まずは標準出力。関数println(もしくはprint)は組み込み関数です。


C:\ jrunscript
js> println("Hello");
Hello


関数定義、クラス定義
 では、関数の定義と実行。以下の2つは同じ意味ですが、下の方が今風だと思います。


js> function sayHello() {println("Hello");}
js> sayHello();
Hello

js> var sayHello2 = function() { println("Hello2"); }
js> sayHello2();
Hello2


 引数を使う場合は、型指定いりません。


js> var say = function(message) { println(message); }
js> say("Hello3");


 JavaScript でもクラス定義、というかインスタンスの考え方があります。クラスメソッド定義がコンソールでは動かないので、ファイルを作成し、関数load()を使って実行してみます。1行目のようにthis.をつけるとインスタンス関数。2行目のようにprototypeで定義するとクラス関数になります。インスタンス関数はインスタンスごとにメモリ領域が取られますが、クラス関数の場合には1箇所です。Javaの場合には、デフォルトでクラス関数と同じ動きをします。


<ファイル class.js>
1 |var myclass = function(){ this.say = function() {println("A");}}
2 |myclass.prototype.say2 = function(){println("B")}
3 |var my = new myclass();
4 |my.say2();

js> load("class.js")
A
B


マップとリスト
 これで基本は終わり。では、マップとリスト。マップは{キー:値,キー:値...}で、リストは[値,値...]。3つの例のように、マップとキーが複雑な場合でも美しく構造が表現できています。これJavaで書こうと思うと、かなりへこみます。


js> var mymap = {aa: "111", bb: "222"};
js> println(mymap.aa);
111

js> var mylist = ["1", "2", "3"];
js> println(mylist[0]);
1

js> var mymap2 = {aa: "111", bb: "222", cc: ["1","2",{hoge:"AAA"}]};
js> println(mymap2.cc[2].hoge);
AAA


関数渡し、戻し
 さ、ここからが気持ち悪い領域です(w。JavaScriptに限らず、多くのスクリプト言語では関数そのものを引数や返値として渡すことができます。 Javaでいえば、"メソッド"を渡しあうようなものです。まずは引数にしてみます。関数callHelloは、引数の関数fnを"Hello"をいう引数で呼び出しています。2行目では、関数printlnをつけて関数callHelloを呼び出します。


js> var callHello = function(fn) { fn("Hello") };
js> callHello( println );
Hello


 では、自分で定義した関数を渡してみましょう。ここでは無名関数を定義しています。このように引数の位置に、いきなり定義することも可能です。これはJavaの無名クラスと同じですね。


js> callHello( function(msg) {println(msg)} );
Hello


 つぎに、返値に設定してみます。返値は初めて出てきましたが、returnで記述します。まず関数pを定義しました。関数pは、関数printlnを返却します。2行目が、気持ち悪いと思いますが、p()という部分で関数をpを呼び出すため返値には関数printlnが取れます。最後の ("Hello") は、この返値として取得した関数printlnを呼び出しているというわけです。下側の記述の方が分かりやすいかもしれません。


js> var p = function() { return println };
js> p()("Hello");
Hello
js>
js> var tmp = p();
js> tmp("Hello")
Hello


 このように関数自体を引数や返値にできる言語を高階関数と読んでいます。JavaScriptは高階関数です。Javaは違います。
 かなり気持ち悪いと思うのですが、僕の場合は()の意味がわかったときに理解ができた気がしました。()は、それがついた変数を"呼び出す"という意味です。だから、全てはオブジェクトであり、()をつければ呼び出せるだけなのです。たとえば、関数pを()をつけないで標準出力すると以下のようになります。これは関数pというオブジェクトそのもののtoString()が処理されているようになるので、処理記述が表示されているわけです。


js> println(p)

function () {
return println;
}


 こう考えると関数定義が、function hoge() {}より、hoge = function() {}が好かれるのかも分かります。関数を定義しているのではなく、関数オブジェクトを定義しているような感じなのです。Javaでいえばメソッドを定義するというよりもクラスを定義する近いのです。


クロージャー
 関数の利点にクロージャーと呼ばれる処理があります。まずコレクション系。これもファイルを作成します。まずクラスArrayにクラス関数eachを追加します(1行目)。関数eachは、自分の配列でループをまわし(2-4行目)、値1つ1つを引数として、引数の関数fnを呼び出します(3行目)。6 行目では配列arrを作成します。[]によるリスト定義は、内部的にはクラスArrayが生成されているのです。8-10行目は配列arrのクラス関数 eachに対して、値の足しこみをする無名関数(9行目)を渡します。最後に11行目では足しこまれた値totalを出力します。


<ファイル closure.js>
1 |Array.prototype.each = function(fn) {
2 |  for ( i=0;i<this.length;i++ ) {
3 |    fn(this[i]);
4 |  }
5 |}
6 |var arr = [1,2,3,4,5];
7 |var total = 0;
8 |arr.each(
9 |  function(v) { total += v; }
10 |);
11 |println(total);

js> load("closure.js");
15


 クロージャーというのは「関数と"そこで使われている変数"(局所変数)をセットにして扱う処理方式」のことです(たぶん...ツッコミ歓迎)。上記の例では7行目の変数totalと、9行目の無名関数はセットです。この無名関数を関数each内で呼び出し(3行目)ても、変数totalにはちゃんとアクセスすることができます。
 もう1つの例をお見せしましょう。フォーマット処理です。関数printlnを拡張した関数exprintlnを考えます。関数exprintlnでは、引数のp(prefix)とs(suffix)を使って標準出力を行なう無名関数を返却しています(2-4行目)。この関数を('Hello')で呼び出すとき(8,9行目)、1行目のpとsが参照できているのです。


<ファイル exprintln.js>
1 |var exprintln = function(p, s) {
2 |  return function(msg) {
3 |             println(p + msg + s)
4 |           }
5 |};
7 |var ep = exprintln('-', '-');
8 |ep('Hello');
9 |exprintln('*', '*')('Hello');

js> load("exprintln.js");
-Hello-
*Hello*


 どちらの例でも"なんとなく見た目どおり"の変数を利用することができます。このスコープをレキシカル・スコープと呼びます(たぶん...ツッコミ歓迎)。


Javaクラスの利用
 最後にJavaクラスを使ってみましょう。Java環境でスクリプト言語を使っているのですから、これができないことにはうれしくありません。クラス Dateを生成して利用してみます。newを使って生成できますが、頭にPackagesをつけます(ただしjavaパッケージの場合は省略も可能)。こうしたオブジェクトを使う場合、Getter/Setterはプロパティのように使うことができます。メソッドgetTimeを使いたい場合は、. timeとすればよいわけです。


js> var d = new Packages.java.util.Date();
js> d.time
1157347832062


 実はJavaとの連携は深く暗い淵があります。例えばJavaのStringとJavaScriptのString扱いが異なることにハマリがちです。 JavaScriptのString同士、あるいはJavaScriptのStringとJavaのStringは==だと同じになりますが、Javaの String同士ではメソッドequalsを使わないといけません。
 またJavaのMapやListも{}や[]とは異なる扱いになります。Sarugau JSではラッパ機構を使って同じにアクセスできるようにしてはいますが、完璧とはいえません。


js> var a1 = new java.lang.String("a");
js> var a2 = new java.lang.String("a");
js> var test = function(x,y){ if ( x == y ){println('eq')} else {println('neq')} };
js> test("a", "a");
eq
js> test(a1, "a");
eq
js> test(a1, a2);
neq


まとめ
 ここで紹介したのはJavaScriptのベーシックな考え方だけです。実際の構文はたくさんあります。try{}catch{}も記述できます。ま、だいたいはJavaに似ているので、そんなには迷うことはないと思います。
 思うにJavaエンジニアがJavaScriptに感じる気持ち悪さは、英語を学んでいる状態かもしれません。単語や構文の問題というよりも思考プロセスの問題。なんで動詞が先なんだよと。しかし、だからこそ英語のメッセージは強力で訴えかけるものがあります。英語が上達するコツは「英語で考え、英語で話す」だという人がいますが、同じようにスクリプト言語が上達するコツは「スクリプト言語で考え、スクリプト言語で書く」ということかもしれません。

トラックバック

このエントリーのトラックバックURL:
http://www.arclamp.jp/mt33/mt-tracback.cgi/1623

コメントを投稿

(いままで、ここでコメントしたことがないときは、コメントを表示する前にこのブログのオーナーの承認が必要になることがあります。承認されるまではコメントは表示されません。そのときはしばらく待ってください。)

About

2006年09月04日 15:50に投稿されたエントリーのページです。

ひとつ前の投稿は「マッシュアップアワードで優秀賞」です。

次の投稿は「第3回atWorks(9/8)[再告知]」です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

Creative Commons License
このブログは、次のライセンスで保護されています。 クリエイティブ・コモンズ・ライセンス.
Powered by
Movable Type