ES6の前にES5のベストプラクティスを改めて考えたい

歴史認識

だいたい以下のような流れだと認識している。

  1. IE8以前を含むECMAScript 3暗黒時代があった
  2. この時代をベースにベストプラクティスが構築 + HTML5ブームが発生した
  3. 暗黒時代なんで結構つらいし、IE9じゃないとECMAScript 5使えないし、結局辛い
  4. CoffeeScriptを筆頭としたaltJS一派に救いを求めた(結局Coffeeは一過性のカフェインだったわけだけど)
  5. 気がついたらECMAScript 6が来そうになっていた
  6. ES6でのsyntaxの拡張・標準ライブラリの増強はカッコいい
  7. 気がついたらES5当たり前、ES6も使えそうな世界が来そうになっていた(希望的観測が多分に含まれているのは暗黙の了解と化している)
  8. ES6、altJSの次のナウい世界として注目を集める

だいたいこんな感じで、ECMAScript 5を用いたベストプラクティス的な物が存在しない、ないしは「全員知っている」という暗黙の了解の下に、明確な話があまり語られずに進んでしまっていると思います。

と、いうわけで、我々はreal productでいつ使えるか不明なES6 syntaxにこだわる前に、ES5を語るべきなのではないかと感じています(IE8以前も割と堂々と無視していい時代になったし)。

で、ES5でどうするの

前段では壮大な構想をぶっていましたが、ES5自体が過去の非互換・不明瞭な挙動を明確に定めたベースラインとしての性質を持っているので、あんまり語ることが無いというが現実とは思います。

use strictを使え

すっかり定着した感はありますが、いいからstrict modeを使え。これが今後の基盤になる。

Object.seal, Object.freezeを積極的に使う

処理系によるJIT最適化時のhidden class (shape)の構造を安定化させる=最適化の余地を増やすという副次効果が存在しますが、そんなものは処理系が最適化パスを入れているか次第なので、ここでは主題とはしません。

この2種類のメソッドを利用するメリットは、オブジェクトの構造をstaticにすることで、予期せぬプロパティの追加や変更を防ぐ点にあります。これにより、単純なtypoによる想定外のプロパティ名の追加・参照や、Read Onlyとなるべき返り値の取り扱いが容易になります。

そして、strictモード環境下では、これらのメソッドを適用したオブジェクトへの不用意な操作に対してエラーを発生させるため、デバッガビリティの向上につながります(というよりもstrictモードが無いとエラーが出ないのでデバッガビリティが著しく低く、むしろ付けない方が良いケースがある)。

Object.preventExtensionでも同様にオブジェクト構造をstaticにすることが可能ですが、ECMAScript 5におけるlazy getterパターンとか使わない限りは、Object.sealで問題ないと思います。

Object.definePropertyでのsetter/getterの動的生成

Vue.jsあたりがこれでデータバインディングを実現していたと認識しているんですが(流し読みしたんで覚えてない)、それに限らず、便利なんで使いましょう。

Function.prototype.bindによる引数を部分適用した関数の生成

クロージャと違ってスコープまたいで束縛できて便利

Arrayの拡張メソッド群

便利。かつてはパフォーマンス上の理由で使うべきではないとされていましたが、今時のエンジンはだいたいself-hostedしてるのでhotなコードであれば最適化可能だし、パフォーマンス的に忌避する理由はない。というよりも、パフォーマンス的にクリティカルな箇所はあとで計測時に発見されて問題になるので、そのときに考えた方が良い。

ES5で間に合わないこと

ES6 Promise

適当にpolyfillで解決しましょう

Module

requireJS使うなり、自分でconcatするなり、TypeScriptさまに全てを解決してもらえばいいのではないか

クラス構文

ES5にそんなものはない。プロトタイプベースで書け。だいたいなんとかなる。

定数宣言

TypeScriptがそこらへん検査・保証してくれるとうれしかった。

アノテーション

Closure CompilerかTypeScript

総論

ES5でも結構まともに開発は可能です。

以上の理由から、筆者はaltJSに頼る必要性をあまり感じていません。

ただ、

  • 軽微なコードにClosure Compilerを使うのは重すぎる
  • 言ってもモジュールは欲しいし、型アノテーションも欲しい(余計なアサーションコード減らせる)

あたりの理由から、TypeScriptの型アノテーション + モジュール機能あたりだけを使って、残りは全部ES5で書くくらいがいい塩梅なのではないかと感じています(TypeScriptの出力する、クラス構文のtrans-pileされたコードが個人的に好きではないというのもある)。

尚、個人的にはES7でTypedObjectの標準化はもちろん、型アノテーションくらいもついてくれると非常にうれしい。

最後に

正直、3年ほど書くのが遅れたという気持ちはある。