Polyfillとは何か、動作原理、APIをエミュレートするライブラリ

著者: IT Sectr 公開日: 2026-05-19 読了時間: 9 分

Polyfill (ポリフィル)は、ネイティブに実装されていない環境で、欠如した機能(API、メソッド、オブジェクト)をエミュレートするコードです。Polyfillを使うと、古いブラウザやランタイムでも、現代的なJavaScript、CSS、Web APIの機能を利用できます。MDN Web Docsによると、polyfillはプログレッシブ・エンハンスメントとクロスブラウザ互換性を確保するための重要なツールです。

まとめ

  • Polyfillは、APIが実装されていない実行環境における欠如したAPIのソフトウェアエミュレーションです
  • core-jsは、すべてのstage-4提案をサポートする現代JavaScript向け標準ポリフィルライブラリです
  • Polyfill.ioは、ユーザーのブラウザだけに動的にポリフィルを提供するサービスです
  • トランスパイル vs polyfill:トランスパイルは構文を変換し(arrow function → function)、polyfillは新しいメソッドを追加します(Array.includes, Promise)
  • 機能検出(Feature detection)は、競合を避けるためにポリフィルをロードする前にネイティブ実装の有無を確認します

Polyfillとは何か?

Polyfillは、実行環境がネイティブにサポートしていない機能を実装するコードの一部です(通常JavaScript)。この用語は、2009年にRemy Sharpが言葉遊びとして考案しました。Polyfillは、壁のヒビを垫める垫剤であるPolyfillaに似ています。Polyfillは、標準と特定のブラウザまたはランタイムでのサポートの間のギャップを垫めます。

Polyfillは既存のコードを変更せず、実行環境を拡張します。ブラウザがArray.prototype.includesをサポートしていない場合、polyfillはメインコードの実行前にこのメソッドをArrayプロトタイプに追加します。Polyfillは、新しいグローバルオブジェクト(Promise, Map, Set, Symbol)、スタティックメソッド(Array.from, Object.assign)、プロトタイプメソッドをエミュレートできます。

機能検出(Feature detection)は、ポリフィルをインストールする前の必須メカニズムです。ユーザーエージェント(どのブラウザか)を確認する代わりに、メソッドの存在を確認すべきです:if (!Array.prototype.includes) { Array.prototype.includes = ... }。これにより、ネイティブ実装がすでに存在する場合に、polyfillがそれを上書きしないことが保証されます。Google Analyticsや他のサービスは、解析のためにAPIサポートデータを収集しています。

ポリフィルの登場

最初のポリフィルは、Internet Explorer 6–8の時代(2005–2009年)に登場しました。ディベロッパーがW3C標準とブラウザ実装の間のギャップを発見したからです。この用語はRemy Sharpが2009年のBarCamp Londonで提案しました。最初のマスポリフィルはhtml5shiv(2009年)でした。これはInternet ExplorerにHTML5タグ(<section>、<article>、<nav>)のサポートを追加するライブラリです。

ES6(2015年)と年次ECMAScript更新サイクルの登場に伴い、必要なポリフィルの数が増えました。毎年、新しいメソッド(Array.includes, String.padStart, Object.fromEntries, Promise.allSettled)が標準に追加されますが、古いブラウザではサポートされません。core-jsは、2014年にes6-shimとして始まり、万能のソリューションとなりました。2026年現在、core-jsにはES5–ES2025向けの5000以上のポリフィルモジュールが含まれています。

ポリフィルできることとできないこと

カテゴリポリフィル可能ポリフィル不可
プロトタイプメソッドArray.includes, String.startsWith
グローバルオブジェクトPromise, Map, Set, Symbol
スタティックメソッドObject.assign, Array.from
言語構文Arrow functions, async/await, class
Web APIfetch, IntersectionObserverService Worker(ネイティブサポートが必要)

Polyfill vs トランスパイル:違いと相互作用

トランスパイルは、新しい構文を古い構文に変換します(const → var、() => {} → function() {})。Polyfillは、欠如したメソッドやオブジェクトを追加します(Promise, Array.includes)。これら二つのメカニズムは互いに補完し合います。トランスパイルは構文的な互換性を実現し、ポリフィルはAPIの完全性を確保します。Babel + core-jsは、完全サポートのための標準的な組み合わせです。

Babel @babel/preset-envのuseBuiltInsオプションは、ターゲットブラウザに基づいて必要なポリフィルを決定します。useBuiltIns: "usage"は、コードで使用されているAPIを解析し、core-jsから必要なポリフィルだけをインポートします。useBuiltIns: "entry"は、単一のcore-js/stableインポートを通じて、ターゲットブラウザに対するすべてのポリフィルをインポートします。

例:Array.prototype.includesのポリフィル

js
// ポリフィルの存在確認と追加
if (typeof Array.prototype.includes !== "function") {
  Object.defineProperty(Array.prototype, "includes", {
    value: function(searchElement, fromIndex) {
      if (this == null) {
        throw new TypeError("Array.prototype.includes called on null or undefined");
      }
      var arr = Object(this);
      var len = arr.length >>> 0;
      if (len === 0) { return false; }
      var start = fromIndex | 0;
      var k = Math.max(start >= 0 ? start : len + start, 0);

      while (k < len) {
        if (arr[k] === searchElement) { return true; }
        k++;
      }
      return false;
    },
    writable: true,
    configurable: true,
  });
}

// 使用方法 — 今はどのブラウザでも安全
const arr = [1, 2, 3, 4, 5];
console.log(arr.includes(3)); // true

Array.prototype.includesのポリフィルは、メソッドがArrayプロトタイプに定義されているかを確認します。定義されていない場合、Object.definePropertyを使って、writable: true、configurable: trueのフラグでプロパティを作成します。実装はES2016仕様に従います:null/undefinedの確認、オブジェクトへの変換、負のfromIndexの処理。ポリフィルを追加すると、Internet Explorer 11を含むすべてのブラウザでarr.includes(3)が動作します。

core-js:標準ポリフィルライブラリ

core-jsは、TC39のすべてのstage-4提案(ECMAScript標準)をサポートする、最も綜合的なJavaScriptポリフィルライブラリです。core-jsには、Promise, Symbol, Map, Set, WeakMap, WeakSet、Arrayメソッド、Stringメソッド、Objectメソッド、Numberメソッド、Mathメソッド、Reflect、globalThis、そしてすべてのstage-4提案のポリフィルが含まれています。現在のバージョンcore-js 3.38+はES5–ES2025をカバーしています。

core-jsは、@babel/preset-envとuseBuiltInsオプションを通じてBabelと統合します。この統合がなければ、ディベロッパーは各ポリフィルを手動でインポートする必要があります:import "core-js/stable/array/includes"。@babel/preset-envは、.browserslistrcからのターゲットブラウザに基づいて、必要なインポートを自動的に追加します。これにより、バンドルサイズが減少します。必要なポリフィルだけが含まれます。

例:fetchのポリフィル

Fetch APIは、最もよくポリフィルされるWeb APIの一つです。ネイティブのfetch実装はChrome 42+(2015年)、Safari 10.1+(2017年)、Firefox 39+(2015年)で利用可能ですが、Internet Explorerや古いWebViewでは利用できません。whatwg-fetchポリフィルは、XMLHttpRequestを介してfetchをエミュレートします。代替案として、isomorphic-fetch(Node.jsとブラウザ向けのポリフィル)や、ポリフィルが不要な万能ライブラリaxiosを使用することもできます。

js
// 古いブラウジだけにfetchポリフィルを読み込む
if (typeof self.fetch !== "function") {
  import("whatwg-fetch").then(module => {
    self.fetch = module.fetch;
    console.log("fetch polyfill loaded");
  });
}

// fetchの使用(ポリフィルとネイティブAPIの両方で動作)
async function loadData() {
  try {
    const response = await fetch("https://api.example.com/data");
    const json = await response.json();
    return json;
  } catch (error) {
    console.error("Failed to load:", error);
  }
}

動的インポート import()を介したfetchポリフィルにより、現代的なブラウザが不要なコードをロードしないことが保証されます。ポリフィルは非同期的にロードされ、メインスレッドをブロックしません。ロード後、self.fetchがネイティブ実装を置き換えるか、欠如した実装を追加します。これはプログレッシブ・エンハンスメントの技術です。現代ブラウザはネイティブコードのみを取得し、古いブラウザは追加のポリフィルを取得します。

core-jsとBabelの統合

js
// babel.config.js — core-js + preset-env
module.exports = {
  presets: [
    ["@babel/preset-env", {
      useBuiltIns: "usage",
      corejs: {
        version: "3.38",
        proposals: true,
      },
      targets: {
        browsers: ["> 0.5%", "not dead", "not op_mini all"],
      },
    }],
  ],
};
none
# .browserslistrc — ターゲットブラウザ
> 0.5%
last 2 versions
not dead
not op_mini all
ie >= 11
not ios_saf < 12

useBuiltIns: "usage"は、コードを解析し、実際に使用されているポリフィルだけを追加します。corejs.versionは、プロジェクトのcore-jsバージョンを指定します。targets.browsersは、ターゲットブラウザの最低レベルを定義します。ターゲットブラウザが古いほど、より多くのポリフィルが含まれます。.browserslistrcはBabelだけでなく、Autoprefixer、PostCSS、Stylelintによっても一貫したターゲティングのために使用されます。

Polyfill.ioと動的なポリフィル読み込み

Polyfill.ioは、ユーザーのブラウザに必要なポリフィルを動的に判別し、それらだけを返すサービス(および同名のライブラリ)です。Polyfill.ioはUser-Agentヘッダーを使用してブラウザバージョンを判別し、最小セットのポリフィルを提供します。これにより、万能ポリフィルバンドルと比較して、データ転送量が減少します。

Polyfill.ioの接続は、メインアプリケーションコードの前に<script>タグを記述します。サービスはUser-Agentを解析し、そのブラウザだけに対するポリフィルを含むJavaScriptファイルを返します。Chromeはポリフィルを何も受け取らず、IE 11は完全セットを受け取ります。現代ブラウザが不要なコードをロードしない、パフォーマンスに最適なアプローチです。

Polyfill.ioの接続

html
<!-- Polyfill.io: 動的読み込み -->
<script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=Promise%2CArray.prototype.includes%2CObject.assign%2Cfetch"></script>

<!-- ローカルのPolyfill.ioバージョン -->
<script src="/js/polyfill.js"></script>
<script>
  // feature detection for fetch
  if (!self.fetch) {
    loadScript("/js/fetch-polyfill.js");
  }
</script>

URLのfeaturesパラメータは、ロードするポリフィルを指定します。可能な値:メソッド名(Array.prototype.includes)、グローバルオブジェクト(Promise)、またはフラグ(es6, es2016)。"default"フラグは、現代JavaScriptのための基本セットをロードします。プロダクションプロジェクトの場合は、独自のCDNでPolyfill.ioをホストするか、ライブラリのローカルバージョンを使用することが推奨されます。

モバイルアプリケーションとWebViewにおけるポリフィル

WebViewモバイルアプリケーション(Android WebView、iOSのWKWebView)は、ポリフィルにとって特殊な環境です。WebViewバージョンは、OSバージョンとインストールされたChrome System WebViewアップデート(Android)またはiOS SafariのWKWebViewに依存します。古いAndroid(4.4、5.0)では、WebViewはChromium 30–37をベースにしており、fetch、Promise、IntersectionObserverをサポートしていません。

React NativeはJavaScriptCore(iOS)またはHermes(Android)を使用します。これらのエンジンはES6+を異なる方法で実装しています。iOSのJavaScriptCoreは大部分のES6機能をサポートしていますが、いくつかのstage-3提案が欠如している場合があります。Hermes(React Native 0.70+でデフォルト使用)は、ES標準の制限されたセットをサポートしており、ポリフィルが必須です。

WebViewのサポート確認

js
// WebView向けfeature detection
const polyfills = [];

// Promise
if (typeof Promise === "undefined") {
  polyfills.push("Promise");
}

// Fetch API
if (typeof self.fetch === "undefined") {
  polyfills.push("fetch");
}

// IntersectionObserver(ラジーロードに必要)
if (typeof IntersectionObserver === "undefined") {
  polyfills.push("IntersectionObserver");
}

// 動的なポリフィル読み込み
if (polyfills.length > 0) {
  const script = document.createElement("script");
  script.src = "https://cdn.polyfill.io/v3/polyfill.min.js"
    + "?features=" + polyfills.join(",");
  document.head.appendChild(script);
}

機能検出をWebViewに適用すると、重要なAPI(Promise、fetch、IntersectionObserver)の存在を確認し、欠如しているものだけ動的にポリフィルをロードします。これにより、現代的なWebView(Android 12のChrome 100+)が不要なコードをロードせず、古いWebView(Android 5.0)が必要なサポートを得られることが確保されます。

よくある質問

React Nativeにポリフィルは必要ですか?

React NativeをHermesで使用する場合、いくつかのESメソッド(Array.flat、Array.flatMap、globalThis、TextEncoder)にポリフィルが必要です。プロダクションビルドにはcore-jsまたはreact-native-polyfill-globalsを含めることが推奨されます。iOSのJavaScriptCoreはより多くの機能をサポートしていますが、stage-3提案にポリフィルが必要な場合もあります。

ポリフィルはパフォーマンスに影響しますか?

ポリフィルは、JavaScript実装がエンジンのネイティブC++実装よりも遅いため、パフォーマンスが1–5%低下します。例えば、純粋JSのPromiseポリフィルは、V8のネイティブPromiseよりも遅いです。ただし、大部分のアプリケーションでは差はほとんどありません。クリティカルなコードでは、機能検出を介してネイティブ実装の有無を確認することが推奨されます。

ポリフィルとトランスパイルの違いは何ですか?

トランスパイルは構文を変換します:const → var、アロー関数 → function。ポリフィルは新しいオブジェクト/メソッドを追加します:Promise、Array.includes、fetch。トランスパイルはビルド時に動作し、ポリフィルはランタイムにロードされます。古い環境で現代コードを完全にサポートするには、両方のメカニズムが必要です。

2026年にポリフィルを省略できますか?

省略できます。ただし、ターゲット集団が現代ブラウザ(Chrome 90+、Safari 15+、Firefox 90+)のみを使用している場合に限ります。古いデバイスや企業ユーザー(Internet Explorer 11はまだ公的主権部門で使用されています)をサポートするプロジェクトでは、ポリフィルが必須です。Google Analyticsでオーディエンスのブラウザ統計を分析してください。

core-jsのポリフィルバンドルのサイズは?

core-jsのフルビルドは約85 KB(gzip)です。BabelでuseBuiltIns: "usage"を使用すると、必要なポリフィルだけが含まれ、ターゲットブラウザに応じてサイズが5–30 KBに減少します。現代ブラウザ(Chrome 100+)では、ポリフィルが全く不要な場合もあります。

まとめ

  • Polyfillは、ランタイム環境で欠如したAPIをエミュレートし、古い環境でも現代コードを動作させます
  • core-jsはES5–ES2025向けの標準ポリフィルライブラリで、@babel/preset-envを介してBabelと統合します
  • Polyfill.ioは、ブラウザのUser-Agentに基づいて動的にポリフィルをロードするサービスです
  • トランスパイル + ポリフィルは綜合的なソリューションです。Babelが構文を変換し、core-jsが欠如したAPIを追加します
  • 機能検出でネイティブ実装を確認してからポリフィルをロードすることでパフォーマンスを維持できます
  • WebViewとHermesでは、古いバージョンではfetch、Promise、IntersectionObserverにポリフィルが必須です

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください