はじめに
Array.prototype.at() や Object.groupBy() のような新しいJavaScriptの機能を使ったら、
古いブラウザや一部の実行環境で is not a function と怒られた経験はないでしょうか。
ECMAScriptは毎年のように新機能が追加されますが、すべての実行環境が即座に対応してくれるわけではありません。
そんなギャップを埋めてくれるのが core-js です。ECMAScript標準やWHATWG/W3Cの仕様に定義された
メソッドを、対応していない環境にも「後付け」してくれるポリフィルライブラリで、Babelやpreset-env
の内部でも使われている、いわばJavaScriptエコシステムの縁の下の力持ちです。
とはいえ、読むより触った方が早いと思います。Setの集合演算や配列の新メソッドが、 importひとつでどう動くようになるのかを先に見ていただけます。
core-jsとは
core-jsは、ECMAScript本体の機能に加え、TC39のプロポーザル段階の機能、 さらにURLやstructuredCloneといったWHATWG/W3C由来の機能まで、 非常に広い範囲をカバーするモジュール化されたポリフィル集です。
Babelの@babel/preset-envやSWCの自動ポリフィル注入機能から内部的に利用されているほか、
単体でも import 'core-js/actual/...' の形で必要な機能だけを読み込めます。
主な特徴
- カバー範囲の広さ - ES2015以降の標準機能から、Promise.tryやSetの集合演算などの 提案段階(プロポーザル)の機能まで幅広く対応しています
- モジュール単位での読み込み - 全部入りの
core-js/actualだけでなく、core-js/actual/array/flat-mapのように機能単位でインポートでき、バンドルサイズを抑えられます - グローバル非汚染版の提供 -
core-js-pureを使えば、Array.prototypeなどの グローバルオブジェクトを書き換えずに、必要な関数だけをインポートして使えます - ビルドツールとの連携 - Babelの
preset-envやSWCのcoreJsオプションから、 ターゲットブラウザに応じて必要なポリフィルだけを自動的に注入できます
インストール
npm install core-js
グローバル汚染を避けたい場合はcore-js-pureを、動作確認用にすべてまとめて読み込みたい場合は
core-js-bundleを利用します。
npm install core-js-pure
npm install core-js-bundle
core-jsのサンプルを動かす
以下は、2つの数値の集合に対してSetの和集合(union)・積集合(intersection)・差集合(difference)
を計算するフォームです。これらはES2025で標準化されたSetの新メソッドで、core-jsの
core-js/actual/set/unionなどをインポートするだけで、対応していない環境でも使えるようになります。
まずは要点だけを抜き出したコードです。
import 'core-js/actual/set/union'
import 'core-js/actual/set/intersection'
const a = new Set([1, 2, 3])
const b = new Set([2, 3, 4])
a.union(b) // Set(4) {1, 2, 3, 4}
a.intersection(b) // Set(2) {2, 3}
a.difference(b) // Set(1) {1}
実際に動かせるものが下です。入力欄の数値を書き換えて、ボタンを押してみてください。
union・intersection・differenceはいずれもES2025で標準入りしたばかりのSetメソッドで、
Node.js 22や最新ブラウザでは既にネイティブ実装されていますが、対応前の環境では
core-jsの該当モジュールをインポートするだけで同じ挙動になります。集合Aを1,2,3,4から
10,20,30に書き換えると、積集合(intersection)が空になる様子が確認できます。
基本的な使い方
もっともシンプルな使い方は、core-js/actualをまるごとインポートすることです。
ES2025までのポリフィルがまとめて読み込まれ、対応していない環境でも新しい構文が動くようになります。
必要な機能だけを読み込みたい場合は、次のように個別のパスを指定します。
import 'core-js/actual/promise/try'
import 'core-js/actual/array/flat-map'
Promise.try(() => 42).then((it) => console.log(it)) // => 42
実践的なユースケース
個別インポートでバンドルサイズを抑える
core-js/actualは便利ですが、使わない機能まで含めると数百KBに膨らみます。
実際のプロダクトでは、使用する機能だけをcore-js/actual/{カテゴリ}/{機能名}の形で
個別にインポートするのが定石です。
Object.groupByもES2024で標準入りした比較的新しい機能で、配列を条件でグルーピングする際に
reduceを自前で書かずに済みます。ボタンを押すと、フルーツの頭文字ごとにグルーピングされた
結果がその場で表示されます。
core-js-pureでグローバル汚染を避ける
core-js/actual系はグローバルオブジェクト(Array.prototypeなど)を直接書き換えますが、
ライブラリやSDKを作る側の場合、利用側の環境に影響を与えたくないケースがあります。
そこで使うのがcore-js-pureです。関数として個別にインポートでき、グローバルは一切変更しません。
import flatMap from 'core-js-pure/actual/array/flat-map'のように関数として受け取り、
flatMap(arr, fn)という形で呼び出している点がcore-js/actual版との違いです。
グローバルを触らないので、他のライブラリとの競合を心配せずに済みます。
Babel・SWCと組み合わせて自動ポリフィルを注入する
実際のプロジェクトでは、core-jsを手動でインポートするより、Babelや SWC のビルド設定に
組み込んで、ターゲットブラウザに必要なポリフィルだけを自動挿入するのが一般的です。
これはビルド時の設定なので、ブラウザ上で直接実行するものではありません。
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": "3.50"
}
]
]
}
useBuiltIns: "usage"を指定すると、コード中で使われている機能を静的解析し、
必要なcore-jsモジュールのimportを自動的に挿入してくれます。たとえば
new Set([1, 2, 3])と書くだけで、ビルド後にはimport 'core-js/modules/es.set.js'が
自動的に追加されます。SWCを使っている場合も、.swcrcのenv.coreJsに同様の設定が可能です。
まとめ
core-jsは、ECMAScriptの最新機能を「今使えるかどうか」で悩む時間を減らしてくれるライブラリです。
core-js/actualでまとめて読み込むもよし、core-js/actual/{機能名}で必要な分だけ絞るもよし、
core-js-pureでグローバルを汚さずに使うもよしと、プロジェクトの事情に合わせて選べる柔軟さが魅力です。
普段Babelやフレームワークの内部で意識せず使っている方も、今回のサンプルのように
単体でインポートして動かしてみると、どの機能がどのモジュールに対応しているのかが見えてきます。
まずは自分のプロジェクトで使っている新しめのメソッドが、core-jsのどのパスに当たるか探すところから
始めてみてはいかがでしょうか。
