« 『JavaScript:The Good Parts』を読んで | メイン | JavaScriptでconcatはもう使うべきではないのかもしれない »

『JavaScript:The Good Parts』にツッコミ

前エントリで、『JavaScript:The Good Parts 「良いパーツ」によるベストプラクティス』が万人向けでないことを書きました。
自分の実力を顧みず、この本のベストでない部分をつっこんでいこうと思います。ゴリアテどころかゴリアテの集団に挑んでいくような状態ですね。
さて、全エントリで書いた通り、この本は悪い本ではなく良本であり、読む人が読むと良い刺激になるに違いないと思っています。これを契機によりよいJavaScriptの書き方について論議が進むのではないかと期待しています。しかし、対象と思われる層が中級者以上で、初級者が読むと逆に悪本になりかねない部分を持っています。勘違いしそうな部分、気になった部分を中心に記述していきます。このため、否定的な内容は沢山出てきますが、上記のような前提ですので、書籍全体がこのような内容が散見されるわけではありません。そして、これを読んで本の内容を理解したと思われても困るので、敢えて本が手元にないと何を言っているのかわからないような書き方をします。以上をご理解の上、読んで頂けるようお願いします。
訳者まえがき
ZIGOROuさん!
はじめに
特になし
1章 良いパーツ
特になし
2章 文法
JavaScriptの文法について「鉄道ダイアグラム」を用いて解説しています。この記述方法はとてもわかりやすいのですが、書く方は大変そうですね。そのせいか「図2-10 命令文」が間違えている気がします。「[ラベル]:[順序破壊文]」の順に書けるように見えますが、このようなコードを見た記憶がありません。いくつか試してみましたが確認できませんでした。
「図2-26 接中辞演算子」では、「==」と「!=」の記述がありません。後々出てきますが、筆者はこの二つの演算子の使用を推奨していません。だとしても、文法上許されている演算子を書かないのは不親切ではないでしょうか。
3章 オブジェクト
特になし
4章 関数
この章ではじめて「あれ?」と感じました。まず「4.3.3 コンストラクタ呼び出しパターン」に、new演算子は「お薦めできる方法ではない。」と書かれています。確かにthisの持つ問題はありますがnewを使うなというのは言い過ぎではないでしょうか。これではJavaScriptがぐっとLispよりの言語になってしまいます。Java側から見れば、「無名関数は使うな」と言いたくもなるでしょう。JavaのようにもLispのようにも書くことができるのがJavaScriptの強みのひとつだと思っているのですが、この強みを削ぐことになりかねません。
そして、この本で一番疑問に思ったのが「4.15 メモ化」です。フィボナッチ数を求める関数にメモ化の仕組みを導入するのは非常に納得がいきますが、memoizerの部分が納得いきません。メモ化を共通化するのが目的となってしまっています。memoizerに渡す無名関数は確かに簡潔に見えます。しかし、この無名関数を見て、読解できる人がどれくらいいるのでしょうか。この書籍が目指しているはずの簡潔な記述にはなっていないと思います。
5章 継承
重箱の隅をつつきますが、「5.2 オブジェクト指定子」の最初のサンプルコードと次のサンプルコード、引数の数が違っています。
6章 配列
「6.5 配列かどうかの判定」にある配列の判定関数は、多分これまでに見た中で一番信頼のおける判定をしているように思います。propertyIsEnumerableを利用するのは良いアイディアですね。そもそもpropertyIsEnumerable自体ほとんど見かけることがありません。以前のKanasan.JSで話題になっていたはずです。そもそも「typeof [] === "array"」 が真を返すような仕様であれば済む話なのですが...。次のバージョンでそうはならないでしょうかね...。
「6.7 次元」では初期値を指定した配列生成のメソッドがありますが、次元を固定したものではなく次元数可変に対応したものが欲しいところです。
7章 正規表現
自分でもなんでこんな細かいことに気づいたのか不思議なくらい細かい話です。「図7-2 parse_number」の「[文字列の開始点]---(-)---[数字]---[文字列の終了点]」と並んでいる「[数字]」と「[文字列の終了点]」の間の直線ですが、図の下の方に迂回する線があるため不要です。
「7.2 正規表現の構築」にある「単一のインスタンスを共有するようになる」という記述は少々不正確のようです。コードを書いてFirebugで動かして確認しましたところ、確かに「var f=function(){return /123/;};var a=f();var b=f();」とした場合のlastIndexは同じ値でした。しかし、「var a=/123/;var b=/123/;」とした場合は同じとはなりませんでした。理由はわかりませんが、別のインスタンスのようです。
8章 メソッド
この章は普段使っているメソッドのあまり知られていなさそうな機能について書かれています。
Arrayのconcatとpushとunshiftは引数が複数でも可能とは知りませんでした。これは便利ですね。
Numberのところにはメソッドが4つ書かれていますが、toStringしか覚えていませんでした。知らないでいたら文字列編集でやっているでしょう。
RegExp.execのこのような細かい挙動は全く知りませんでした。String.matchとレシーバと引数が逆になっているだけのメソッドと思っていました。正規表現を駆使してやるよりはこのやり方が可読性は高いのですが、人によっては冗長と感じるかもしれません。速度的にはどうなんでしょうか。
Stringは忘れていたメソッドの使い方や知らなかったメソッドが非常に多くありました。indexOfの第二引数とsearchの存在とsplitの第二引数はすっかり頭から抜け落ちていました。matchメソッドでgフラグの指定の有無で、キャプチャ効果が変わってくるというのも経験則でしかもっていませんでした。replaceのドル文字シーケンスは知っていれば便利なのではないでしょうか。自分だと第二引数に無名関数を使用して解決しそうですが、ドル文字シーケンスが利用できる場面では簡潔な記述になるはずです。splitメソッドの引数にキャプチャグループありの正規表現を指定したときは、戻り値の配列にキャプチャグループ由来の文字列が含まれるのは全く知りませんでした。splitの条件に指定する文字列を残しておきたいと思うことはありますので今後は多用しそうです。locateCompareメソッドは知られていないのではないでしょうか。
9章 スタイル
特になし
10章 美しい機能たち
特になし
付録A ひどいパーツ
この付録では、問題を抱えているにも関わらず、使用を避けることができないパーツについて説明しています。
「A.10 NaN」の重箱の隅です。「isNaN」が「isNan」になっています。
「A.11 偽の配列」にある配列判定処理ですが、「6.5 配列かどうかの判定」にある配列判定関数と少し違っています。spliceの判定が抜けています。実際はどちらの処理が良いのでしょうか。
「A.14 オブジェクト」のこの問題は思いつきませんでした。多数の値の中に一致する値が存在するかどうか調査するような状況では、配列に値を格納し先頭から一致しているか調べるような処理にしてしまった場合、処理数が多くなりどうしても遅くなってしまいます。このためハッシュ検索と呼ばれている方法が使われます。その他に、それぞれの値の出現回数を調べる際にも使用されます。このハッシュ検索で、値が既出かどうか判定するif文の判定式はどのように書くべきでしょうか。もしかして「if(hash[str]){...」と書いていないでしょうか。JavaScriptの標準的な書き方をしていればそのように記述してしまいますが、これは大きな過ちを含んでいます。strに代入する値の中に、もし「constructor」の文字が含まれていた場合、予想外の動きをしてしまいます。これを防ぐ方法として、hasOwnPropertyを使う方法とtypeofを使う方法が書かれていますが、私は前者をお勧めします。typeofを使う方法では一見したところ何をしたいのかわかりません。hasOwnPropertyであれば処理内容が明白です。そして値の存在チェックの場合、ハッシュ内には何を入れても良いため、必ずしも型が一致するとは限らないからです。
付録B 悪いパーツ
この付録では、使ってはならないパーツを詳解しています。よく読むと使ってはならないとはおらず、「使わずに済ませることが容易なもの」となっていますが、この本全体の調子が強いため多くの人が「使ってはならない」と捉えるのではないでしょうか。
この書籍のfor文は「for (i = 0; i < n; i += 1) {...」のような記述になっています。「i++」ではなく「i += 1」です。これはインクリメント演算子とデクリメント演算子は使用すべきではないという著者の主張のためです。ここまでそれなりの説明がされてきたひどいパーツと悪いパーツですが、明確な説明はなされていませんでした。そして「B.7 ++ --」でも納得のいく説明がされていません。あげられた理由は次の三点、「コードが詰まりすぎる」、「トリッキーなものになりすぎる」、「不可解になりすぎる」です。Cのコードを例示し、「++」と「--」は無謀な記述をさせる原因となっており、無謀な記述はバッファオーバーランを招いていると主張しています。待ってくださいと言いたいです。今、論議の的になっているのはJavaScriptです。ポインタを使ったCのサンプルコードで、ポインタのないJavaScriptの記述を議論するのでしょうか。これがこの書籍で二番目に大きな疑問をもった場所です。
ここで説明がされているのは「使ってはならないもの」ではなく「使わずに済ませることが容易なもの」とわかっていれば良いのですが、前者で捉えていた場合、「B.8 ビット演算子」は納得のいかない部分になります。例えば、マスク処理では「&」を使った方が直感的でしょう。これをビット演算子を使わずに処理しようとすると手間がかかりますし、多分遅くなるでしょう。「絶対に使ってはならない」のではないとわかっていれば、ビット演算子を使うべき場面で躊躇することはないのではないでしょうか。
「4.3.3 コンストラクタ呼び出しパターン」でも書きましたが、「b.11 new」でもnewについて使うべきではないと書かれています。前述の通り、Javaのような書き方ができなくなります。一番の問題は、日時を表すリテラルがないJavaScriptで「new Date()」という書き方ができなくなってしまうと日付を扱うことができなくなってしまうことです。
Kanasan.JSで初めて知ったのですが、実はJavaScriptではundefinedは定数ではなく変数です。変数のため「var undefined=true;」と書くことが可能です。つまり「if(o.p===undefined){...」という判定があった場合、思いもしない動きをすることがあります。では、undefinedは使うべきではないのでしょうか。全てのコードが自分の管理下にある場合は、undefined変数の値を変更しなければ良いのですから使用しても構わないと思います。しかし、ライブラリを作成している場合、どのような状況で使われるのかわからないのですが、undefinedを使いたい場面も存在すると思います。undefinedは変数名としてだけではなく、概念上は値としても存在します。どのようにしてその値を取得できるのでしょうか。例えば「var f=function(){return;};var undefined=f();」と書くことでundefined変数にundefinedという値が代入されます。undefinedを使用したいスコープ内に記述すれば、他のスコープへの影響もありません。しかしこれでは冗長すぎます。voidをしようすれば「var undefined=void(0);」のように書くことが可能です。明らかに可読性が上がっています。「B.12 void」ではvoidの利用を推奨していませんが、そもそもvoidを利用する局面が少ないため余り気にする必要はないと思います。
付録C JSLint
JSLintは、この書籍の著者であるDouglas Crockford氏によるJavaScriptの文法チェックと検証を行なうツールです。付録CはまるでJSLintの説明書のようになっています。読んでいる限りでは相当便利なツールに見えます。ここまで強烈なまでに使用すべきパーツかどうかを主張してきた筆者ですが、この検証ツールはそこまでのルールは強要しません。オプション指定により検証内容を変更することができます。ルールを緩く設定することで、これまでの記述スタイルを余り崩す必要はないかもしれません。逆に、著者の主張に厳格なスタイルでなければエラーとなるようにすることも可能なようです。特記すべきは、各々のJavaScriptファイル内にコメントとしてルールを記述することにより、ルールをファイル別に変更可能ということです。例えば、ライブラリとそれ以外ではコーディングスタイルが違っていても、JSLintはコメント内のルール指定により柔軟に検証内容を変更してくれるということです。機会があれば是非使ってみたいと思います。
付録D 鉄道ダイアグラム
特になし
付録E JSON
最後に重箱の隅。「E.3 JSONパーサ」のescapeeオブジェクトの中のrとtの間のカンマが抜けています。また、escapeeの中の「[名前]:[値]」の左の位置が揃っていません。本当にどうでもいいですね。
ツッコミの感想
ここまでまとまりのない長文にお付き合い頂きありがとうございました。最初にも説明しましたが、『JavaScript:The Good Parts 「良いパーツ」によるベストプラクティス』が手元になければ、あまり理解できないようにしています。この文章がこの書籍の購買を抑制するようなものであってはいけません。しかし、書籍の値段、薄さ、そして中をぱっと見ただけでは非常に簡単なように思えるため、今からJavaScriptを勉強しようとする初心者が思わず手をとってしまいそうです。しかし初心者にとっては毒となるような記述も多く含まれています。反対にJavaScript中級者には非常に刺激的な内容ではないかと思います。しかし「本当にこの書き方は悪い書き方なの?」と不安になるかもしれません。他の誰かの意見を聞きたくなるかもしれません。実際私もそうでした。これから読む人がこのような不安を払拭できるようにするために、「私はこう考えました。」と明らかにしたほうがよいのではないかと思いました。書籍を片手に参考にして頂けたらと思います。JavaScriptのベストプラクティスを論議するための出発点となりそうな書籍ではないかと思いますのでお勧めします。
この本の表紙に描かれている蝶は「カバマダラ」という品種で、その美しさは学者や蝶愛好家を魅了してきたそうです。その美しさとは裏腹に、奇しくもその体内には毒をもっているそうです。まさにこの本の表紙に打って付けではないでしょうか。

トラックバック

このエントリーのトラックバックURL:
http://www.kanasansoft.com/cgi/mt/mt-tb.cgi/222

コメントを投稿

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

Google