JavaScript の論理演算子と短絡評価の話。

ちょっとしたきっかけで JavaScript の文法について気になったので、ちょっとだけ調査してみたことを少し書く。

本題に行くまでの長い前フリ

そもそもの話で言うと、先週の土曜日にVSハッカソン倶楽部主催の TypeScriptハッカソン2Days ってイベントっつーか、勉強会っつーかに参加して TypeScript の話以上に JavaScript の話が気になったことがあったところからなのな。

@k_maruさんを先生にして TypeScript のハンズオン(体験学習)があって、TypeScript の環境作りから導入までを一通り学んだ。
で、そんなかで TypeScript でモジュール化するって内容があって

具体的には
test.ts

module App {
  var button:any = document.getElementById("#sample");
}

↑この TypeScript ソースコードコンパイルすると↓こういう JavaScriptソースコードが生成されるって話。

var App;
(function (App) {
    var button = document.getElementById("#sample");
})(App || (App = {}));

JavaScript に慣れてない人やC言語系譜からの JavaScript つかいの人には直感的じゃ無い構文になってるけど、言葉で書けば「無名関数の定義と呼び出しを同時に行ってる」形になる。
無名関数を普通の形にして、俺っちにとって平易に書きなおせば

var App;

function foo(app) {
  var button = document.getElementById("#sample");
}

foo(App);

↑こんな感じになる。
ただし、 foo関数の呼び出し引数は、TypeScript からコンパイルした奴は論理演算子(||)になってるけどね。
だけど標準論理演算子(|)じゃ無くて、短絡評価演算子(||)なんだぜ?
JavaScript の暗黒面の端っこがちょっと見えたよね?

短絡評価(ショートサーキット評価)

まずは不正確ながら簡単に書く。

論理式 T or F は T になる。くどく書けば F or T も T になる。どっちが TRUE ならば TRUE になるのな。ここまで読んでる人になら書くまでも無いお話。

C言語由来のプログラミング言語(C++/Java/C#など)は OR を表す演算子は2種類ある。

で、 は単純な論理演算子(logical operators)、 は「条件OR演算子(conditional OR expression)」なんて小難しく定義されてる。

実際に書くコードでは if 文の中に書くのが || で、ビット演算(論理演算)の式として書くのが | ってことになる。

コンピュータの世界では、実行時性能を考慮して複数の論理式は左から解釈していくことが多い。(と思う)
要するに、|| (短絡評価)の場合であれば True である確率が高い順に左から並べておけば、他の真理値を確認する(解釈する・評価する)必要が無いので実効性能上有利になる。

擬似コードを書けば
2つの真理関数(述語)の AND(&&) を使う複合命題があるとして

if (isOutOfRange(condition) && validate(condition)) {
  //何がしかの処理
}

この場合、左側の関数の戻り値が false であれば、右側の関数は呼び出されない。これが短絡評価。
逆に、論理演算子で書くと

if (isOutOfRange(condition) & validate(condition)) {
  //何がしかの処理
}

2つの真理関数を呼び出した結果得られる2つのブール値の AND をとることになる。つまり、2つの関数は実際に必要かどうかは抜きにして2つとも必ず呼び出されることになる。

・・・思いつきで書いてるから説明になってないな、まあ wikipedia のショートサーキット評価の項を参照してください。

短絡評価
http://ja.wikipedia.org/wiki/%E7%9F%AD%E7%B5%A1%E8%A9%95%E4%BE%A1

で、その wikipedia の説明に面白いことが書いてある。

JavaScript では短絡評価した結果の最後に評価した値が返るんだってさ!しかも、仲間のスクリプト言語もそんな仕様らしいぜ!

関数型言語では return 文を省略して値を定義したら、それが戻り値になる的な話があって、Scala では if 文じゃ無くて if 式とかって言う。ざっくり言っちゃえば if 文が3項演算子みたいなもんってことな。
同じような理屈で、JavaScript では短絡演算子が3項演算子みたいなもんてことになる。
※↑ここの3行メチャクチャざっくり書いてんなあ。

で、この辺の JavaScript での論理演算子のきちんとした説明を、てっく煮ブログさんがまとめてくれてる。

論理演算子(&& と ||)を応用する
http://tech.nitoyon.com/ja/blog/2007/12/27/cmpop/

最初のコードに戻る

var App;
(function (App) {
    var button = document.getElementById("#sample");
})(App || (App = {}));

App || (App = {}) の部分は、App が false と評価されないのであれば App が引数として渡る、そうじゃ無い場合は App = {} の結果が渡る。
JavaScript で {} は new Object() と等価なので

var App = new Object();

var App = {};

も同じ意味になる。(クラスが無い JavaScript において new Object() の評価はいろいろと考える余地があるみたいやけどね)
ちなみに、[] は new Array() と同じ。

var App の App ってシンボルがモジュール名っつーか名前空間を表す意味でしか無いので、実際は null でもなんでも良いんやけど、変数シンボルの評価時に undefined になるのを避ける意味で App || (App = {}) と書くらしい。

@k_maruさんがおっしゃってたけど、TypeScript は JavaScript のベストプラクティスから知見を得たモダンな JavaScriptコードを生成してるらしい。なので TypeScript の勉強はJavaScriptの学習にも良いってのは俺っちも共感出来る。

ま、実際問題 JavaScript を書いてるプログラマのどれくらいの人が JavaScript の暗黒面を認識してるのか、または落とし穴に落ち込まないように慎重にコーディングしてるのかはわからないけど、大規模な JavaScript を書く必要が出てくる前に取り組まなアカンことは多い。
ウェブアプリケーションのUI周りで jQuery とイベント処理ぐらいなら問題にならん話も、もうちょっと奥まで足を踏み入れるとなんやかんやでしんどいのな。

クロージャとか、関数の巻き上げとか、プロタイプベースのイブジェクト指向とか・・・ね。

個人的にはプロトタイプベースのオブジェクト指向はすげーわかりにくいんで、今回 TypeScript をちょっとだけ触ってみて、静的な型情報やクラスの構文を拡張する TypeScript には better JavaScript としての可能性を強く感じたのな。
JavaScript の関数言語型の性質を薄めちゃう可能性もあるかも知れないけど、個人的にはコンパイル時に解決出来る問題はコンパイル時に解決して欲しいんだよねえ。

本格的に TypeScript に取り組むのはもっと先になるかも知れないけど、JavaScript の学習の材料に使う可能性は高いと思うのな。
食わず嫌いの人はちょっとやってみるのも良いと思うで!