古いブラウザに自動で polyfill を適用する

古いブラウザへのes6対応です。

古いブラウザにも一応対応したい。ただし、煩わしいことはしたくない。最新ブラウザでpolyfillによるコード量増加などの悪影響を防ぎたい。とかの場合に使うといいかも…。

ただし、HTML5やCSS3、JavaScriptの最新構文を使用できるわけではないです。polyfillなので…。

polyfill.io の中国企業への売却

上記の Issue の通り。 polyfill.io ドメインは、中国の CDN ベンダーに売却されました。そのため、政治的なリスク等から polyfill.io の使用を不安視する声が上がっています。

それに伴い、 Cloudflare / Fastly が polyfill.io のフォークを提供することを発表しています。本記事では、コードを Cloudflare のコードに差し替えることで対応します。

コード

以下のコードを他<script>より先(先頭)に宣言する。

特に何も気にしない場合

<!--script src="https://polyfill.io/v3/polyfill.min.js?features=default%2Ces5%2Ces6"></script-->
<script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=default%2Ces5%2Ces6"></script>

最新ブラウザでスクリプトの外部読み込みによるパース解析をブロックしたくない場合

polyfill-loader.js/**
 * polyfill-loader.js
 * 同期的にES6のpolyfillを読み込む(IE9+対応)
 */
(function() {
  // アローファンクション構文有無(簡易ES6判定)
  function canUseArrowFunction() {
    try {
      Function('x=>1');
      return true;
    } catch (e) {
      return false;
    }
  }

  // スクリプト同期ローダー
  function scriptSynchronousLoader(url) {
    // IE7+
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, false);
    xhr.send(null);

    if (xhr.status === 200) {
      var script = document.createElement('script');
      script.text = xhr.responseText;
      var sc = document.getElementsByTagName('script')[0];
      sc.parentNode.insertBefore(script, sc);
    }
    xhr = null;
  }

  function main() {
    if (!canUseArrowFunction()) {
      // 他にほしいpolyfillがあれば、URLを変更する
      // see https://polyfill.io/v3/url-builder/
      //scriptSynchronousLoader('https://polyfill.io/v3/polyfill.min.js?features=default%2Ces5%2Ces6')
      scriptSynchronousLoader('https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=default%2Ces5%2Ces6')
    }
  }

  main();
})();

補足

polyfill

polyfill.ioを利用しています。polyfill.ioは、リクエストの User-Agent ヘッダーから、要求元ブラウザに適した polyfill を返してくれます。アクセスしたブラウザを認識して必要最小限の polyfill を自動で返してくれます。

IE8

IE8でObject.definePropertyが非DOMオブジェクトのプロパティに対する取得/設定関数はサポートされていません。そのため、polyfillの置き換えの際にエラーとなります。そのため、IE9+でしかpolyfillが効きません。(たとえ、Object.definePropertyのpolyfillを先に適用しても、DocumentFragment, Element, getOwnPropertySymbolsで問題が発生します)

es5対応

// strictモード有無(簡易ES5判定)
function canUseStrictMode() {
  return (function() {"use strict"; return typeof this;}).call(1) === 'number';
}

※IE9-を判定する

Babel

コード量の増加や事前コンパイルできる環境であれば、Babelを使用してもいいかも。

参考