【1日目】Strict mode と JSLint で書く Titanium Mobile

この記事は @astronaughts (通称・あすとろなんとか) さん主催の Titanium Mobile Advent Calendar 2012 向けに書いています。 Early Advent Calendar とは異なり小ネタ以外もご自由にとのことなので、参加したい方は ATND へ !

さて、1日目を担当することになったわけですが、ちょろっと硬いネタでスタートを切らせてもらおうと思います。多分どこかでタガが外れると思うので…。今回は Titanium Mobile を Strict mode で書いてみましょう。

Titanium Mobile & JavaScript

今更ですが、Titanium Mobile は JavaScript を使う開発ツールじゃないですか。 Appcelerator からも JavaScript コーディング規則が提示されていますが、人によって規則が変化しがちなプログラミング言語だと思います。

1つの目的を実現するために複数の手段が用意されていて、どちらを使うべきなのかが曖昧です。 Alloy のようなフレームワークの登場でアプリはこう書くべしという1つの解答は示されましたが、中身のコードはこう書くべしが定まりません。

Titanium 3.0 の地味な変化の1つに Strict mode への対応があります。 Android の V8 エンジンは 2.x 系でも対応していましたが、 3.0 系で初めて iOS も対応になりました。 Strict mode を使うとこれまでなぁなぁにされてきた箇所がエラーとして取り扱われるようになり、 JavaScript を処理するエンジンも最適化しやすくなるコードになるので速度の向上も見込める … そうです。

早速 Strict mode を試してみましょう。

Strict mode

iOS シミュレータ (iOS 6.0 Retina 4inch) を使い、 Titanium Mobile SDK Continuous Builds (3.0.0.v20121130200208) を使ってみます。

Window の上に TableView があり、100個の Row とクリックしたらアラートが表示されるだけのコードです。

もちろん、このコードはこのまま app.js として保存すれば動くコードですが、 var win = Ti.UI.createWindow(); の上に “use strict”; を付けてみましょう。

文字列リテラルとして “use strict”; を書くだけです。

するとどうでしょう。なんと for 文の直下にある function 文でエラーが出ています。

これは Strict mode として正しい動きです。なぜならば、 function 文 (関数宣言文) は言語仕様的には for の中に書いてはいけないからです。きちんと言語的な正しさを Strict mode を有効にすることで認識してくれていることが分かります。

では、 for 文の中で関数を定義したい場合はどうするのかといえば、関数リテラルを使います。

これで良いのです。また、 arguments.callee が使えなくなります。例えば、

こういうコードがあったとします。ラベルをクリックしたらアラートが表示されるだけですが、このクリックを1度限りにするため、 removeEventListener を使ってイベントの登録を解除しています。

この際に arguments.callee を渡すと取り囲んでいる関数そのものを指してくれるのですが、 Strict mode ではこれは許されません。

匿名関数は JavaScript の便利機能ですが、こういう場合は名前を付けましょう。他にも関数の引数を delete を使って削除したり、8進数を使ったり、 eval で定義した変数は呼び出し元のスコープに漏れ出なかったり、他にも色々あります。

では、既存のコードを Strict mode に対応させるにはどうしたら良いでしょうか。

JSLint

JSLint を使ってみましょう。 JavaScript: The Good Parts の著者でもある Douglas Crockford 氏のツールですが、これを使うと、最初はショックを受けるほどたくさんのエラー出力が拝めるでしょう。 Good Parts 以外は全て悪として切り捨てます。 Strict mode 前提でバリデーションが可能です。

Web 版の JSLint でも良いですが、 npm 経由で JSLint を使うと便利です。

例えば、一番初めの例を JSLint にかけてみます。

エラーはきすぎて途中で止まりました(´;ω;`)

まずは Ti なんてものは定義されてないよ … とか、 var 宣言はまとめてね … とか書かれています。なので最初はこれら2つに対処してみましょう。

トップに global Ti というコメントが入りました。これは JSLint にグローバルオブジェクトとして Ti を使いますよという宣言をするものです。早速チェックします。

まだまだエラーは出ていますが、最後まで走りきりました。今度は ++ 使うな … とか、関数宣言文をこの場所で使うな … とか、 alert なんて定義されてないよ … とか、色々とエラーが出ています。

これに対処してみたいと思いますが、 ++ 使うなは結構厳しいと感じる人もいると思います。そういう場合は JSLint 側に「これは使うよ」という宣言が可能です。同様に alert も使いますよーという宣言を入れてまずは対処をしてみます。

こうなりました。先頭行に jslint に宣言するコメントが追加されていて、さらに for 文の中で var 宣言していたものが全て所属する関数の先頭行にまとまりました。 JavaScript は現在のところ関数スコープを持つ言語で、 for 文や if 文の {} によるブロックスコープが使えないためです。

Titanium Mobile でも let 文、 let 式、 let 宣言が使えれば良いのですが、まだ使えないため、こうしてスコープごとにまとめるか、 (function () {}()); を使ってインスタントにスコープを作るしかありません。

いちいちスコープごとに var をまとめるのが面倒という場合もありますので、そのときは先頭行のコメントを、

このようにしてくださいね。 vars: true を入れればスコープ内で複数の var 宣言を使ってもエラーは出なくなります。この時点でのエラーは、

です。あと少し。ループの中で関数を作るなと怒られています。というわけで、ループの外に関数を逃がしてあげましょう。

さて、このような形になりました。 click 関数が関数リテラルから関数宣言文に戻っています。即時関数の直下ならば関数宣言文は書けるんですね。この click 関数の仕事は関数を返す関数になりました。

引数として受け取った値を閉じ込めて、関数を返しています。クロージャと高階関数を使っていますが、メモリが解放されないことによるメモリリークには気をつけましょう。さて、エラーは …

なくなりました(^ω^)

Strict mode にも準拠し、 JavaScript の言語仕様的にも Good Parts で構成されたキレイなプログラムコードになったと思います。 JSLint は若干やり過ぎな感じもしますが、個人的にはできるだけ対応させたいと思っています。柔軟すぎる言語仕様から、厳密なサブセットだけを使うように心がければ、速くて保守性の高いコードになると思います。バランス良く取り入れてみてください。

明日は @hoyo1111 さんです!

CODESTRONG!