JavaScript、奥が深すぎる

今まで JS 関連のことをぽつぽつエントリにしていたけど、それにはみ出るトピックを羅列する。

いちばん印象的だったところ

関数について考えること。いまのところ、JavaScript を復習するにあたってこれがいちばんの大本になると思う。プログラミング全般というか、それこそ手続き型に対して関数型とあるけれど、こうしていろいろやってみるとオブジェクトのプロパティに関数をいれておけるということ、それを使い倒せるというのがどれだけ世界を広げることなのかっていうのが、こう、すこし世界が広がった感がある。同時にいろいろついていけない感もある。

あとは JavaScript をクライアントサイドで使ってると歴史的背景としても(そして自分のプログラミングのきっかけが JS というのもあって)DOM 操作をいかにするかってことを考えてしまって整理がつかなくなる。それを「JavaScript が言語として提供している分野」「DOM を扱うという意味での JavaScript」「サーバーサイドの JavaScript」というのを意識して分けて考えるきっかけになったと思う。

JavaScriptテクニックバイブル ~効率的な開発に役立つ150の技

JavaScriptテクニックバイブル ~効率的な開発に役立つ150の技


パーフェクトJavaScript (PERFECT SERIES 4)

パーフェクトJavaScript (PERFECT SERIES 4)


JavaScript徹底攻略 (WEB+DB PRESS plus)

JavaScript徹底攻略 (WEB+DB PRESS plus)

関数が関数を扱うということ

JS に限らない話で Python でもそうでなくてもなんでもそうだけど、特に JS をいままで扱ってて感じた。関数が関数を引数にとる。関数が関数を返す。関数が関数を合成する。こういったものを高階関数と呼ぶらしい。f(x,y)を f(x)(y) とさせるのがカリー化とするらしい。Python ではよくあるけどデコレータも関数をラップして扱うようなもの。クロージャはとりあえず JS においては状態をもった関数を作成できる。

関数ってのはいくらかの処理をかためたものでそれを再利用しやすくしたりするっていう意味はもちろんあるんだけど、それだけじゃなくて、関数が柔軟に扱えるということは処理をフックしたりラップしたりメソッドチェインさせたりできる。いまの自分の理解だとあまりこれ以上はおもいつかないけど、だいぶ黒魔術ではないが幅が広がる話で、ものすごく柔軟にさせている。これは直感*1

ところでクロージャの話はパーフェクト JavaScript の解説はすごかった。ちょっと正直よくわからない。いまは表面的につかえればいいやというところ。

プロトタイプチェーンの終端

前に JavaScript の名前空間になぜ Function Object をつくるんだろうって話 - atas でぷととタイプチェインの話を書いたけど、たぶん、一番誤解していたのは「プロトタイプチェーンの終端は Object」って思ってたところで、そうではなくて、「Object.prototype」だ。だから Object.create(Object.prototype) すると「純粋なオブジェクトが得られる」というのは、Object 自体は Object.prototype をもつから純粋ではない。というのがいまのところの理解。

メソッドチェインと再帰について

前に いまさら JSDeferred と $.Deferred を少しさわったメモ - atas というのを書いたけど、jQuery でもメソッドチェインで処理をつなげてかけるのがよい。で、具体的には $ にメソッドを生やせば $.func として使え、$.fn = func としてメソッドを生やせば $(selector).func としてつかえる。実装をみてはいないけど、たぶん内部的にはなんらかの状態を変化させた jQuery Object を返しているんだと思っている。

再帰という考え方は知っていても使えることはなかった。でもこういう実装が再帰的なのかなと思っている。関数型言語をちゃんとやったことはないけど、「for ループの代わりに再帰をつかうと考えるとわかりやすい」と本にあったように、for でループさせないならば、再帰的に参照してぐるぐるまわすしかない。

そういうところが似ているなぁと感じた。冒頭の「関数が重要」というのは、ここらへんをみてみて感じたところでもある。

プロパティとアトリビュートについて

JS としてはプロパティを持っていて、DOM としてはアトリビュート(属性)を持っている。クラスベースのオブジェクト指向だとフィールドとメソッドをあわせてメンバーと呼ぶっぽいけど、JS だとあくまでプロパティに関数が入っているものが便宜的にメソッドと呼ばれているらしい。

混同しやすいところは、DOM を触るときもプロパティアクセスっぽい感じで記述できるというところだ。具体的には img.src = path とか location.href とかそういう。それらはプロパティアクセスっぽいし実際そうかけるのは楽で直感的だけど、たぶん、JS と DOM を区別して考えようとするときに混乱する。

命名とか名前とかなんて便宜的なものでどうでもいいというかもしれない。でも、JS が DOM をさわるクライアントサイドの言語としての意味合いとサーバーサイドで実行されることもある現在、そしてクラスベースではなくてプロトタイプベースであるのもあって、JS が提供している部分と DOM を扱う部分は別に考えたほうが長期的には意味があると思っている。

同期と非同期って話と、functionかcallbackかは別

処理が同期であるか非同期であるかってのと function なのか callback なのかは別だ。たしかに setTimeout や xhr で飛ばしたあとの関数は callback で非同期だけど、リスナーでイベントを登録された関数は callback だけど同期的に処理されると考えていいとおもう。あんまり厳密に考えなくてもいいのかもしれないけど、なんだろう、意識の違いかな。

名前空間と関数オブジェクト

JavaScript の名前空間になぜ Function Object をつくるんだろうって話 - atas とかいたけど、実際たしかに Object でもいいパターンはあるようだ。でも、たいがいなにか名前空間として渡したいものって関数だし、関数だと call/apply/bind が使えるからそのほうが便利だよねとか。これは直感的に感じた。

JS でも内部を隠したい話

UserScript とか GreeseMonkey とかならともかく、名前空間としてグローバルに1つだけ追加したら別に中身は隠さなくてもいいのでは?と思っていたけど、それでもあくまで private にもっておきたいものは JS でも匿名関数 + クロージャで隠蔽してしまうのが筋のいいやり方なのかなぁと。

構文について

JS は文と式で成り立っている。式は式と演算子で成り立っている。というところまではよかったけど、もっと突き詰めていくと式は演算子リテラルと識別子(変数)になる話。たぶん自分は識別子と演算子ってあたりまでは区別がついてたけど、リテラルってのはまた別物として分けられるんだなぁと。

型変換について

「== の比較は型変換がおこるからバッドなやりかた」とは知ってたから避けるようにはしてたけど、でも if (hoge) したときには型変換がおこっている。静的型付けと動的型付けの話は正直まだそこまでよくわかっていないけど、少なくても JS においても型はあるし、それを動的に変換したり暗黙的に変換することがある。ということは頭の片隅においておこうとおもった。

map, filter, リスト内包、ジェネレーター

map, filter は ES5, リスト内包, ジェネレーターは ES6 な内容だけど、 Python からはいったからそこらへんはとっつきやすかった。ただ JS 以外の言語を知らない状態でやってたらだいぶいろいろ苦しかった気がする。

というかジェネレーターオブジェクトの自作すごい

パーフェクト JavaScript にあったけど、ジェネレーターオブジェクトを実装してみるってところ、すごい。

iframe で子要素孫要素でコールバックさせるテク、すごい

わりと古典的というか XHR2 の前はそういうことを駆使していたっぽいんだけど、初めて知った。ざっくりいうと親 window に対してクロスドメイン通信したい対象を iframe でおいておく(子iframe)。で、その子iframe から受け取った値は親 window と同じドメインの孫 iframe に通信させる。で、その孫 iframe から親 window にコールバックさせて値をとる。すごい。

Node におけるカスタムイベント

JavaScript 関連のちょっとしたメモ - atas でカスタムイベントのことを書いたけど、Node だとそこらへんは emit って形でそもそも提供してるんですね。Socket.io さわったから知ってはたけど、そもそもカスタムイベントはその emit で管理してしまうものなんだなぁと。Node あたりの詳細はあんまり追いかけてないので知らなかった。

状態(state)で考える GUI MVC パターンはいいね

GoF な話らしいけど、具体的に「if でフラグをたくさんもたせたコードがどうみてもスパゲティにしかならないよね」ってときに「それは状態で考えてつくるといいよ」というのはとても実践的だった。デザパタな話は昔座学的に読んで概念としては知っててもどう実際現場で使えるのかよくわからないってことが多いので、ひとつの参考になった。

正規表現の (?:hoge|moge) はマッチしてもつかわないという意味

JS とは関係ないかも。グループしたときに (hoge|moge) ってやるとそれらを RegExp.$1 とかでとれるってのはしってたけど、別にマッチした値をあとで使いたいわけじゃないときはこうして (?:hoge|moge) としたほうが筋がいいとおもう。

まあ

かなりざっくりとかいたけど、「それはちゃうだろ」ってところがあったらコメントか Twitter かなんでもいいのでそれとなく伝えてくれるとうれしい。JavaScript をナメてたつもりはないけど、だいぶすごい。言語として限定した上でも、プログラミング、奥が深すぎる。正直ついていけない。

*1:そういう意味では Python において lambda が貧弱って話をきくけど、そういうことなのかなぁと感じる