序章

Webの初期の頃、Webサイトは主にHTMLCSSで構成されていました。 JavaScriptがページに読み込まれた場合、それは通常、効果と対話性を提供する小さなスニペットの形式でした。 その結果、JavaScriptプログラムは、多くの場合、完全に1つのファイルに書き込まれ、 script 鬼ごっこ。 開発者はJavaScriptを複数のファイルに分割できますが、すべての変数と関数は引き続きグローバルスコープに追加されます。

しかし、 Angular React Vue などのフレームワークの出現により、Webサイトが進化し、デスクトップアプリケーションの代わりに高度なWebアプリケーションを作成する企業とともに、JavaScriptが登場しました。ブラウザで主要な役割を果たします。 その結果、一般的なタスクにサードパーティのコードを使用し、コードをモジュラーファイルに分割し、グローバル名前空間の汚染を回避する必要性がはるかに高くなります。

ECMAScript 2015 仕様では、 modules がJavaScript言語に導入され、 importexport ステートメント。 このチュートリアルでは、JavaScriptモジュールとは何かとその使用方法を学習します importexport コードを整理します。

モジュラープログラミング

モジュールの概念がJavaScriptに登場する前は、開発者がコードをセグメントに編成したい場合、複数のファイルを作成し、それらに個別のスクリプトとしてリンクしていました。 これを示すために、例を作成します index.html ファイルと2つのJavaScriptファイル、 functions.jsscript.js.

The index.html fileは、2つの数値の合計、差、積、商を表示し、の2つのJavaScriptファイルにリンクします。 script タグ。 開ける index.html テキストエディタで、次のコードを追加します。

index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <title>JavaScript Modules</title>
  </head>

  <body>
    <h1>Answers</h1>
    <h2><strong id="x"></strong> and <strong id="y"></strong></h2>

    <h3>Addition</h3>
    <p id="addition"></p>

    <h3>Subtraction</h3>
    <p id="subtraction"></p>

    <h3>Multiplication</h3>
    <p id="multiplication"></p>

    <h3>Division</h3>
    <p id="division"></p>

    <script src="functions.js"></script>
    <script src="script.js"></script>
  </body>
</html>

このHTMLは変数の値を表示します xyh2 ヘッダー、および以下のこれらの変数に対する操作の値 p 要素。 The id 要素の属性は、 DOM操作に設定されます。これは、 script.js ファイル; このファイルは、の値も設定します xy. HTMLの詳細については、HTMLシリーズを使用してWebサイトを構築する方法を確認してください。

The functions.js ファイルには、2番目のスクリプトで使用される数学関数が含まれます。 を開きます functions.js ファイルを作成し、以下を追加します。

Functions.js
function sum(x, y) {
  return x + y
}

function difference(x, y) {
  return x - y
}

function product(x, y) {
  return x * y
}

function quotient(x, y) {
  return x / y
}

最後に、 script.js ファイルはの値を決定します xy、それらに関数を適用し、結果を表示します。

script.js

const x = 10
const y = 5

document.getElementById('x').textContent = x
document.getElementById('y').textContent = y

document.getElementById('addition').textContent = sum(x, y)
document.getElementById('subtraction').textContent = difference(x, y)
document.getElementById('multiplication').textContent = product(x, y)
document.getElementById('division').textContent = quotient(x, y)

これらのファイルを設定して保存した後、ブラウザでindex.htmlを開くと、すべての結果を含むWebサイトを表示できます。

いくつかの小さなスクリプトがあるWebサイトの場合、これはコードを分割する効果的な方法です。 ただし、このアプローチに関連するいくつかの問題があります。

  • グローバル名前空間の汚染:スクリプトで作成したすべての変数—sum, difference、など-windowオブジェクトに存在するようになりました。 と呼ばれる別の変数を使用しようとした場合 sum 別のファイルでは、すべて同じ値を使用しているため、スクリプトのどの時点でもどの値が使用されるかを知ることが難しくなります。 window.sum 変数。 変数をプライベートにする唯一の方法は、変数を関数スコープ内に置くことでした。 の間に競合が発生する可能性もあります id 名前の付いたDOMで xvar x.
  • 依存関係の管理:正しい変数が使用可能であることを確認するために、スクリプトを上から下に順番にロードする必要があります。 スクリプトを別のファイルとして保存すると、分離のように見えますが、基本的には単一のインラインを使用するのと同じです。 <script> ブラウザページで。

ES6がJavaScript言語にネイティブモジュールを追加する前に、コミュニティはいくつかの解決策を考え出そうとしました。 最初のソリューションは、すべてのコードをオブジェクトまたは即時呼び出し関数式(IIFE)に記述し、それらをグローバル名前空間の単一オブジェクトに配置するなど、バニラJavaScriptで記述されました。 これはマルチスクリプトアプローチの改善でしたが、グローバル名前空間に少なくとも1つのオブジェクトを配置するという同じ問題があり、サードパーティ間でコードを一貫して共有するという問題を簡単にすることはできませんでした。

その後、いくつかのモジュールソリューションが登場しました。 CommonJS Node.js Asynchronous Module Definition(AMD)で実装された同期アプローチ。非同期アプローチ、および Universal Module Definition(UMD)。これは、以前の両方のスタイルをサポートするユニバーサルアプローチであることが意図されていました。

これらのソリューションの出現により、開発者はパッケージ npm にあるような、配布および共有できるモジュールの形式でコードを共有および再利用することが容易になりました。 ただし、多くのソリューションがあり、JavaScriptにネイティブなものはなかったため、ブラウザーでモジュールを使用するには、 Babel Webpack Browserifyなどのツールを実装する必要がありました。

複数ファイルアプローチには多くの問題があり、提案されたソリューションは複雑であるため、開発者はモジュラープログラミングアプローチをJavaScript言語に導入することに関心を持っていました。 このため、ECMAScript2015はJavaScriptモジュールの使用をサポートしています。

モジュールは、他のモジュールが使用する機能を提供し、他のモジュールの機能に依存できるようにするためのインターフェイスとして機能するコードのバンドルです。 モジュールをエクスポートしてコードを提供し、をインポートして他のコードを使用します。 モジュールは、開発者がコードを再利用できるようにし、多くの開発者が使用できる安定した一貫性のあるインターフェイスを提供し、グローバル名前空間を汚染しないため便利です。

モジュール(ECMAScriptモジュールまたはESモジュールと呼ばれることもあります)がJavaScriptでネイティブに利用できるようになりました。このチュートリアルの残りの部分では、モジュールを使用してコードに実装する方法について説明します。

ネイティブJavaScriptモジュール

JavaScriptのモジュールは importexport キーワード:

  • import:別のモジュールからエクスポートされたコードを読み取るために使用されます。
  • export:他のモジュールにコードを提供するために使用されます。

これを使用する方法を示すために、 functions.js ファイルをモジュールにして、関数をエクスポートします。 追加します export 各関数の前にあり、他のモジュールで使用できるようになります。

次の強調表示されたコードをファイルに追加します。

Functions.js
export function sum(x, y) {
  return x + y
}

export function difference(x, y) {
  return x - y
}

export function product(x, y) {
  return x * y
}

export function quotient(x, y) {
  return x / y
}

script.js、使用します import からコードを取得するには functions.js ファイルの先頭にあるモジュール。

import 他のコードの前に常にファイルの先頭にある必要があり、相対パスを含める必要もあります(./ この場合)。

次の強調表示されたコードをに追加します script.js:

script.js

import { sum, difference, product, quotient } from './functions.js'

const x = 10
const y = 5

document.getElementById('x').textContent = x
document.getElementById('y').textContent = y

document.getElementById('addition').textContent = sum(x, y)
document.getElementById('subtraction').textContent = difference(x, y)
document.getElementById('multiplication').textContent = product(x, y)
document.getElementById('division').textContent = quotient(x, y)

個々の関数は中かっこで名前を付けてインポートされることに注意してください。

このコードが通常のスクリプトではなくモジュールとして読み込まれるようにするには、次のように追加します。 type="module"script のタグ index.html. を使用するコード import また export この属性を使用する必要があります:

index.html
...
<script type="module" src="functions.js"></script>
<script type="module" src="script.js"></script>

この時点で、更新を含むページをリロードできるようになり、Webサイトはモジュールを使用するようになります。 ブラウザのサポートは非常に高いですが、 caniuse を使用して、どのブラウザがサポートしているかを確認できます。 ファイルをローカルファイルへの直接リンクとして表示している場合は、次のエラーが発生することに注意してください。

Output
Access to script at 'file:///Users/your_file_path/script.js' from origin 'null' has been blocked by CORS policy: Cross-origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.

CORSポリシーのため、モジュールはサーバー環境で使用する必要があります。サーバー環境は、 http-server を使用してローカルにセットアップするか、ホスティングプロバイダーを使用してインターネット上でセットアップできます。

モジュールは、いくつかの点で通常のスクリプトとは異なります。

  • モジュールはグローバルに何も追加しません(window)スコープ。
  • モジュールは常に厳密モードです。
  • モジュールは1回しか実行されないため、同じファイルに同じモジュールを2回ロードしても効果はありません。
  • モジュールにはサーバー環境が必要です。

モジュールは、ブラウザーのサポートと追加機能を強化するためにWebpackなどのバンドラーと一緒に使用されることがよくありますが、ブラウザーで直接使用することもできます。

次に、あなたはいくつかのより多くの方法を探求します importexport 構文を使用できます。

名前付きエクスポート

前に示したように、 export 構文を使用すると、名前でエクスポートされた値を個別にインポートできます。 たとえば、この簡略化されたバージョンを見てください functions.js:

Functions.js
export function sum() {}
export function difference() {}

これにより、インポートできます sumdifference 中括弧を使用した名前:

script.js
import { sum, difference } from './functions.js'

エイリアスを使用して関数の名前を変更することもできます。 これは、同じモジュール内での名前の競合を回避するために行うことができます。 この例では、 sum 名前が変更されます adddifference 名前が変更されます subtract.

script.js
import {
  sum as add,
  difference as subtract
} from './functions.js'

add(1, 2) // 3

呼び出し add() ここでの結果が得られます sum() 関数。

を使用して * 構文では、モジュール全体の内容を1つのオブジェクトにインポートできます。 この場合、 sumdifference 上のメソッドになります mathFunctions 物体。

script.js
import * as mathFunctions from './functions.js'

mathFunctions.sum(1, 2) // 3
mathFunctions.difference(10, 3) // 7

プリミティブ値、関数の式と定義、非同期関数クラス、およびインスタンス化されたクラスは、識別子があればすべてエクスポートできます。

// Primitive values
export const number = 100
export const string = 'string'
export const undef = undefined
export const empty = null
export const obj = { name: 'Homer' }
export const array = ['Bart', 'Lisa', 'Maggie']

// Function expression
export const sum = (x, y) => x + y

// Function definition
export function difference(x, y) {
  return x - y
}

// Asynchronous function
export async function getBooks() {}

// Class
export class Book {
  constructor(name, author) {
    this.name = name
    this.author = author
  }
}

// Instantiated class
export const book = new Book('Lord of the Rings', 'J. R. R. Tolkien')

これらのエクスポートはすべて正常にインポートできます。 次のセクションで説明するもう1つのタイプのエクスポートは、デフォルトのエクスポートと呼ばれます。

デフォルトのエクスポート

前の例では、複数の名前付きエクスポートをエクスポートし、それらを個別に、または1つのオブジェクトとしてインポートし、各エクスポートをオブジェクトのメソッドとしてインポートしました。 モジュールには、を使用してデフォルトのエクスポートを含めることもできます default キーワード。 デフォルトのエクスポートは中括弧でインポートされませんが、名前付き識別子に直接インポートされます。

たとえば、次の内容を functions.js ファイル:

Functions.js
export default function sum(x, y) {
  return x + y
}

の中に script.js ファイルの場合、デフォルトの関数を次のようにインポートできます sum 次のように:

script.js
import sum from './functions.js'

sum(1, 2) // 3

インポート中にデフォルトのエクスポートに名前を付けることができる制限がないため、これは危険な場合があります。 この例では、デフォルトの関数は次のようにインポートされます。 difference それは実際には sum 関数:

script.js
import difference from './functions.js'

difference(1, 2) // 3

このため、名前付きエクスポートを使用することがしばしば好まれます。 名前付きエクスポートとは異なり、デフォルトのエクスポートには識別子は必要ありません。プリミティブ値自体または無名関数をデフォルトのエクスポートとして使用できます。 以下は、デフォルトのエクスポートとして使用されるオブジェクトの例です。

Functions.js
export default {
  name: 'Lord of the Rings',
  author: 'J. R. R. Tolkien',
}

これを次のようにインポートできます book 次のように:

script.js
import book from './functions.js'

同様に、次の例は、匿名の矢印関数をデフォルトのエクスポートとしてエクスポートする方法を示しています。

Functions.js
export default () => 'This function is anonymous'

これは次のようにインポートできます script.js:

script.js
import anonymousFunction from './functions.js'

2つの名前付き値とデフォルト値をエクスポートするこのモジュールのように、名前付きエクスポートとデフォルトのエクスポートを一緒に使用できます。

Functions.js
export const length = 10
export const width = 5

export default function perimeter(x, y) {
  return 2 * (x + y)
}

これらの変数とデフォルトの関数を次のようにインポートできます。

script.js
import calculatePerimeter, { length, width } from './functions.js'

calculatePerimeter(length, width) // 30

これで、デフォルト値と名前付き値の両方がスクリプトで使用できるようになりました。

結論

モジュラープログラミングの設計手法により、コードを個々のコンポーネントに分割して、コードを再利用可能で一貫性のあるものにすると同時に、グローバル名前空間を保護することができます。 モジュールインターフェイスは、ネイティブJavaScriptで実装できます。 importexport キーワード。

この記事では、JavaScriptのモジュールの歴史、JavaScriptファイルを複数のトップレベルスクリプトに分割する方法、モジュラーアプローチを使用してそれらのファイルを更新する方法、および importexport 名前付きおよびデフォルトのエクスポートの構文。

JavaScriptのモジュールの詳細については、MozillaDeveloperNetworkのModulesを参照してください。 Node.jsのモジュールを調べたい場合は、Node.jsモジュールの作成方法チュートリアルをお試しください。