第 5 版 JavaScript[2]

http://d.hatena.ne.jp/janus_wel/20081112/1226763769 の続き。 5 章 式と演算子まで。

  • division by zero
    • Infinity / -Infinity が返る。 C/C++, Perl に慣れていると例外が飛ばないので戸惑うかも。てか例外とんだ方がキレイに書けると思うんだけどなぁ。
  • remainder operator
    • 小数が使える。見慣れないから何に使うのかわからないんだよな…。てか :echo 5 % 1.2 とかのよく使われそうな計算でも誤差が出てくるのはどうなんだ。まぁ内部表現上仕方ないとは思うけどだったら整数限定でいいというか誤差を嫌ってあんま使わなくなると思うんだけど。
    • あとたまに剰余演算を modulo arithmetic っていったり剰余演算子を mod にしてたり % にしてる場合でもmodulo operator って呼んでたりするけど、 modulo ってそもそも合同式 ( http://ja.wikipedia.org/wiki/%E5%90%88%E5%90%8C%E5%BC%8F ) の概念なので整数しか使えないはず。だから modulo 演算で小数を扱えるというのはおかしいんだけど…、ってもしかして小数でも modulo 演算って定義されてるのか ?
  • 等値と同値
    • 日本語だとわかりにくい。どっちが等値でどっちが同値だ。ということで英語がどんな感じか調べてみたんだけどやっぱりよくわからない。等値 = equipollent で同値 = equivalence かな… ? 原著だとなんて書いてあるんだろう。
    • とりあえず == でいいんだけど同一の基本データ型同士だとわかってる場合や自前で型変換をやった場合には === を使うと速くなるよということでいいかも。でもこれってよく考えると型を意識しまくってるよな。知らないひとが使う場合にはとりあえず == でおkという意味では型を意識しなくていいのかもしれないけど。
    • == の場合オブジェクト -> 文字列 -> 数値という感じで最終的に数字に向かうように変換されるみたいだけど、これはあまり信用しない方がいいというか覚えてもしょうがないというか。オブジェクト -> 文字列への変換時に通る valuOf や toString が変更されてる可能性もあるし文字列 -> 数字への変換時の基数っていくつなんだという疑問も。とりあえず試してみたら :echo '010' == 8 は false と出たので 8 進表記とは解釈しないみたいだけど :echo '0xf' == 15 が true と出るので 16 進表記なら理解できるらしい。実装によって変わるのかなぁ ? やっぱり型を意識して明示的に揃えた方がトラブルは少なそうだね。
    • オブジェクトや関数の場合は参照が比較されるということだけど値を比較したい場合は自分で作るしかないのかな。 clone を改修すればできそうだけど名前は何にすべきだろう ? deep equal ?
  • lesser than ?
    • < や > でも文字列比較ができる。 Perl の感覚だと lt や gt を使いたくなってしまう。文字列同士を比較してるんだなってわかる Perl の書き方はけっこう偉大だと思った一瞬。 JavaScript は文字列と数値を比較すると勝手に数値に変換してしまうしね。
  • sum or concatenation
    • + 演算子を数値の加算にも文字列連結にも使える、と。数値と文字列同士の場合は数値が文字列へ変換されて演算されるということなんだけど比較演算子と正反対の性質だなぁ。妥当かどうかはしらないけど要注意だね。個人的には明示的に変換かけてから + 演算子を適用すべきかなと思う。 Number#toString() 関数 ( https://developer.mozilla.org/ja/Core_JavaScript_1.5_Reference/Global_Objects/Number/toString ) と parseInt() 関数 ( https://developer.mozilla.org/ja/Core_JavaScript_1.5_Reference/Global_Functions/parseInt ) でそれぞれちゃんと基数を指定してやって変換をかけるという方法ね。
    • てかここらへんの仕様を見てると少ない演算子で直感的に書けることを優先しているように思える。それに対して Perl はより厳密な意味の演算子を多数用意してちゃんと書きわけてくださいというデザインだね。最終的には好みだと思うけど暗黙的な型変換が行われているかどうかソースから読み取るスキルというのは明示的な型認識・変換やコンテキストの把握よりも習得コストが高いと思うので Perl のほうがわかりやすいと思う。てか Perl って特定の機能に対する表現の仕方は多様だけど式レベルではけっこう厳密なんだなぁと思っちゃったよ。
  • short circuit
    • && と || は short circuit 評価 ( https://developer.mozilla.org/ja/Core_JavaScript_1.5_Reference/Operators/Logical_Operators#.e3.82.b7.e3.83.a7.e3.83.bc.e3.83.88.e3.82.b5.e3.83.bc.e3.82.ad.e3.83.83.e3.83.88.e8.a9.95.e4.be.a1 ) をしてくれるらしい。複雑だから使うなって書いてあるけどそんなに複雑じゃないぞこれ。 Perl like に condition && statement って書くのはやめとけっていうのも大概偏見だと思うしいまどきの scripting language なら大体の言語で short circuit 評価はしてくれると思うんだけど。
    • 有用なのはデータに大きな偏りがあるとき。例が悪いけどたとえば各国語対応だけど日本でサービス開く場合は if (isTopPage && isJapanese) ってコードのが効率的とかそういうこと。最近のサービスはトップページ覗かなくても大丈夫な作りになってるのが多いってのと、日本でやってるサービスなら日本語で表示することが多いっていうおれのイメージなんだけど。このサービスにアクセスするユーザはかなりの確率でトップ以外のページを開くので isTopPage の部分で偽になって isJapanese の部分は評価されないのでその分速いとかってこと。まぁ効果的かどうかといわれるとんー、としか言えないけど。
  • op=
    • += とか -= は評価すると何を返すのかってハナシ。 :echo let a = 0; let b = a += 1; b; の結果が 1 なので演算した結果を返すんだね。いや自分でこういうコード書こうとは思わないんだけどひとのコード読めないのはアレなので。
  • ternary operator
    • 3 項演算子。単項は unary 、 2 項は binary らしい。というのはいいとして if else とどっちが速いのか測ってみたんだけど大きな違いはないみたい。多段で使う場合でも JavaScript だと switch の使い勝手もいいのでどっちを使うかは好みになると思う。てか可読性考えると 3 項演算子涙目な結果だなぁ。 if else に比べるとタイプ数が少ないってくらいしか使う理由ないかも。
/*
 * Executed:     10000 times
 * Average time:  0.05 msec
 * Total time:    0.52 sec
 * */
let a = 1, b = 0; if (a === 1) ++b; else --b;
/*
 * Executed:     10000 times
 * Average time:  0.05 msec
 * Total time:    0.51 sec
 * */
let a = 0, b = 0; if (a === 1) ++b; else --b;
/*
 * Executed:     10000 times
 * Average time:  0.05 msec
 * Total time:    0.50 sec
 * */
let a = 1, b = 0; a === 1 ? ++b : --b;
/*
 * Executed:     10000 times
 * Average time:  0.05 msec
 * Total time:    0.52 sec
 * */
let a = 0, b = 0; a === 1 ? ++b : --b;

function ternary(arg) {
    return arg === 'a' ? 1
         : arg === 'b' ? 2
         : arg === 'c' ? 3
         : 4;
}

function switcher(arg) {
    switch (arg) {
        case 'a': return 1;
        case 'b': return 2;
        case 'c': return 3;
        default:  return 4;
    }
}

/*
 * Executed:     10000 times
 * Average time:  0.06 msec
 * Total time:    0.64 sec
 * */
function ternary(arg) { return arg === 'a' ? 1 : arg === 'b' ? 2 : arg === 'c' ? 3 : 4; } ternary('a');
/*
 * Executed:     10000 times
 * Average time:  0.06 msec
 * Total time:    0.64 sec
 * */
function ternary(arg) { return arg === 'a' ? 1 : arg === 'b' ? 2 : arg === 'c' ? 3 : 4; } ternary(1);

/*
 * Executed:     10000 times
 * Average time:  0.06 msec
 * Total time:    0.65 sec
 * */
function switcher(arg) { switch (arg) { case 'a': return 1; case 'b': return 2; case 'c': return 3; default:  return 4; } } switcher('a');

/*
 * Executed:     10000 times
 * Average time:  0.06 msec
 * Total time:    0.65 sec
 * */
function switcher(arg) { switch (arg) { case 'a': return 1; case 'b': return 2; case 'c': return 3; default:  return 4; } } switcher(1);