JavaScript における import/export/exports の違い、および ES Modules の簡単な歴史

JSで外部モジュールを import・export する場合、 import/export/require/exports など色々な書き方があり、どの場合にどれが使われるのか自分の理解が浅いと感じたので、勉強しなおした。

本記事は備忘としてまとめたもの。後半は ES Modules の話に脱線している。

ES Modules か CommonJS か

大前提として、モジュールのインポート・エクスポートの書き方は ES ModulesCommonJS、プロジェクトがどちらの方式を採用しているかによって異なる。

  • ES Modules の場合は import / export
  • CommonJS の場合は require / exports / module.exports

ES Modules

ECMA Script Modules の略。
クライアントサイドJS、つまり一般的なWebブラウザがサポートしているのはこちらである。

しかし 最近は サーバーサイドJS である Node.js も ES Modules に対応してきており、私個人の体感としても サーバーサイドでも ES Modules を採用することが一般的になっているように思う。

CommonJS

サーバーサイドJS、つまり Node.js が元々サポートしているのはこちらである。

Webブラウザ環境 以外(主にはサーバーサイド)におけるJavaScriptの各種仕様を定めることを目標としたプロジェクト。

(自分用の属人的な)結論

携わるプロダクトの ほぼ全てが(たまたま)ES Modules である自分用の結論としては

  • importexport を基本的に使用する
  • exports は便利なのだが これは CommonJS のものなので使用不可

export の宣言方法 および import による受取方法

default export

  • 1つのモジュール内に1回だけ使える
  • CommonJS の module.exports に相当するものだが、 module.exports のように複数のモジュールを一気に出力はできない
default export
// deafult export in /Functions/Common.ts
// deafult は 1モジュール内に1回しか宣言できないが、後述の 名前つきexport は default とは別に何個も宣言可能
// 以下のどの書き方でもいい
export default () => {}
export default function () {}
export default function getParam () {}
export default "1"

// 上記の default export で定義したものは、以下で受け取る test の部分は何でもいい
// getParam で宣言していても、好きな名前で受け取ることができる(test の部分は getParm でなくても何でもいいということ)
import test from '/@Functions/Common'

named export (名前つきexport)

  • 1つのモジュール内で何回でも使える
  • default export を定義していても、named export を(0以上の任意の数)定義することが可能
named export
// 名前つきexport in /Functions/CommonNamed.ts
export const getParam = () => {}
export function setParam () {}
export const param = "2"

// 上記の 名前つきexport で定義したものは、以下で受け取る
// 宣言した変数名で受け取らないといけない
import { getParam } from '@/Functions/Common';
import { setParam, param } from '@/Functions/Common';

// default export と 名前つきexport 両方を受け取る場合の書き方
import test, { getParam, setParam, param } from '@/Functions/Common';
// 以下のように分けても可
import test from '@/Functions/Common';
import { getParam, setParam, param } from '@/Functions/Common';

ES Modules の歴史と仕様(バージョン)

ES Modules 自体の理解も浅かったので、この機会に(軽くだが)勉強した。

歴史は Wikipedia に概略がまとめられている。

ES Modules では、バージョンごとに利用可能な関数や書き方が異なり、新しいバージョンほど より便利な関数や書き方が利用できるようになっている。

ES5以前

  • ES1〜ES3 は 1997年〜1999年の間、毎年バージョンが1上がっていた
  • ES4は公開されることはなかった
  • ES5 は 2009年に登場した

ES6以降

ES6以降は、仕様名に発行年が付与されることになり、これ以降は毎年新しいバージョンが登場している。 ES6 = ES2015 であり、現在は ES2023 までが登場している。

個人的なバージョンごとの見どころをまとめると

  • ES2015: ES6と同義
  • ES2017: 今では当たり前の async/await は(ECMAScriptでは)ここが初出
  • ES2020: null合体演算子、オプショナルチェーンなどが使えるようになり null との付き合い方に柔軟性が追加された
  • ES2021: 何かと便利な replaceAll は(ECMAScriptでは)ここが初出
  • ES2022: Top Level await、 private method, variable, static variable などが利用可能に
  • ES2023: 現在の最新だが 正直そんなに目ぼしい機能は・・・という感じ

各ESバージョンの変更点・新機能などを見ていて思ったのは

  • 開発効率を下げないために必要な最低ラインは ES2020, ES2021(ないと実装がごちゃつく)
  • より便利に開発するためには ES2022(あるとより安全に書ける)

ということで、 自分でバージョンを自由に選べるならば ES2021 が最低ラインかな、と。

どの ESバージョンを採用すべきか

前提として、Node.js のバージョンによって 利用可能な ES Modules バージョンが異なる。
具体的な対応状況は https://node.green を見ることで、知ることが可能。

個人的には ES2021 までは 開発効率維持のために採用したいため、逆算すると Node.js は(上記の https://node.green にて ES2021 の機能が全て green である) 16.10.0 以降 の採用が必要となる。

参考

執筆日:
本記事のタグ