一つのDEEP DIVE要約(モジュール)


モジュール


モジュールの一般的な意味


モジュール(module)は、アプリケーションを構成する個別要素であり、再利用可能なコードセグメントを指す.
一般的に、モジュールは機能によってファイルユニットに分割されます.このとき,モジュールを成立させるには,モジュールに独自のファイルスキャン(モジュールスキャン)が必要である.
独自のファイルスキャン機能を持つモジュールの資産(モジュールに含まれる変数、関数、オブジェクトなど)は、基本的に非公開です.すなわち、独自のファイルスキャンを持つモジュールのすべての資産がカプセル化され、他のモジュールからアクセスできません.すなわち,モジュールは独立して存在し,アプリケーションから分離される.
しかし、アプリケーションから完全に分離された独立したモジュールは再利用できないため、存在する意味がありません.
したがって、モジュールは開示が必要な資産に限定され、明示的に選択的に開示することができる.これを出口と言います.
公開(export)の資産は、他のモジュールで再利用できます.(依存性あり)
この場合、公開モジュール資産を使用するモジュールをモジュールユーザ(moduleconsumer)と呼ぶ.モジュール・ユーザーは、モジュール公開(export)の資産の一部または全部を選択し、自分のリポジトリにロードして再使用できます.これをimportと言います.

JavaScriptとモジュール


JAvascriptは,Webページを扱う簡単な補助機能の限られた用途を目的として誕生した.
このような生まれつきの限界のため,他のプログラミング言語に比べて不足している.代表的なのは、モジュールシステムがサポートされていないことです.
すなわち、JavaScriptでは、モジュール作成に必要なファイルスキャンとインポートエクスポートはサポートされていません.
また、クライアントであるJavaScriptは、scriptタグを使用して外部のJavaScriptファイルをロードすることができますが、各ファイルには独立したファイルスキャンはありません.
すなわち、JavaScriptファイルを複数のファイルに分割してscriptラベルとしてロードしても、分離されたJavaScriptファイルは最終的には1つのJavaScriptファイル内のように動作します.
したがって、分離されたJavaScriptファイルのグローバル変数が繰り返されるなど、いくつかの問題が発生する可能性があります.
JavaScriptはクライアント側,すなわちブラウザ環境の汎用性に限定されない傾向にあるため,この場合の提案は①CommonJSと②AMD(AsynchronousModuledefinition)である.
JavaScriptランタイム環境ノード.jsはモジュールシステムの事実上の標準CommonJSを採用し、独立したアップグレードを経て、現在CommonJS規範とは異なるが、基本的にCommonJS規範に従う.
Node.jsはECMAScript標準仕様ではありませんが、モジュールシステムをサポートします.だから、ノード.js環境では、各ファイルに独立したファイルスキャン(モジュールスキャン)があります.

ES 6モジュール(ESM)


この場合、ES 6はクライアントJavaScriptで実行されるモジュール機能も追加される.
ES 6モジュール(以下、ESMモジュールと略す)はtype="module"ツリー図をscriptラベルに追加した後、ロードしたJavaScriptファイルをモジュールとして操作する.
ESMが一般的なJavaScriptファイルではないことを明確にするために、ESMのファイル拡張子にはmjsを使用することをお勧めします.
<script type="module" src="app.mjs"></script>

モジュールスキャン


ESMには独立したモジュールスキャンがある.ESM以外の通常のJavaScriptファイルをスクリプトラベルとして別々にロードすると、独立したモジュールスキャンはありません.
// 📁 foo.js
// x 변수는 전역 변수다.
var x = "foo";
console.log(window.x); // foo
// 📁 bar.js
// x 변수는 전역 변수다. foo.js에서 선언한 전역 변수 x와 중복된 선언이다.
var x = "bar";

// foo.js에서 선언한 전역 변수 x의 값이 재할당되었다.
console.log(window.x); // bar
<!DOCTYPE html>
<html>
  <body>
    <script src="foo.js"></script>
    <script src="bar.js"></script>
  </body>
</html>
上記の例のHTMLからscriptタグに分離すると、ロードされた2つのJavaScriptファイルの動作は1つのJavaScriptファイル内のようになります.
つまり、グローバルを共有します.したがって、x変数は繰り返し宣言され、意外にもx変数の値が上書きされます.
ESMは、独立したモジュール化スキャンを提供するため、これらの問題を解決することができる.
// 📁 foo.mjs
// x 변수는 전역 변수가 아니며 window 객체의 프로퍼티도 아니다.
var x = "foo";
console.log(x); // foo
console.log(window.x); // undefined
// 📁 bar.mjs
// x 변수는 전역 변수가 아니며 window 객체의 프로퍼티도 아니다.
// foo.mjs에서 선언한 x 변수와 스코프가 다른 변수다.
var x = "bar";
console.log(x); // bar
console.log(window.x); // undefined
<!DOCTYPE html>
<html>
  <body>
    <script type="module" src="foo.mjs"></script>
    <script type="module" src="bar.mjs"></script>
  </body>
</html>
モジュール内で宣言された識別子は、モジュールの外部で参照できません.モジュールスキャンが違うからです.

export


前述したように、モジュールが外部から参照できない場合、すなわち再利用できない場合、モジュールを作成する必要はありません.
したがって、export(公開)キーワードを使用して、必要な資産を外部参照します.
// 📁 lib.mjs
// 변수의 공개
export const pi = Math.PI;

// 함수의 공개
export function square(x) {
  return x * x;
}

// 클래스의 공개
export class Person {
  constructor(name) {
    this.name = name;
  }
}
宣言の前にexportキーワードを付けるのが面倒な場合は、エクスポートするオブジェクトを1つのオブジェクトに整理して、1回にエクスポートできます.
// 📁 lib.mjs
const pi = Math.PI;

function square(x) {
  return x * x;
}

class Person {
  constructor(name) {
    this.name = name;
  }
}

// 변수, 함수 클래스를 하나의 객체로 구성하여 공개
export { pi, square, Person };

import


importキーワードを使用して、他のモジュールで公開されている識別子を自分のモジュールscopeにロードします.他のモジュールからエクスポートされた識別子名でインポートする必要があります.ESMの場合、ファイル拡張子は省略できません.
// 📁 app.mjs
// 같은 폴더 내의 lib.mjs 모듈이 export한 식별자 이름으로 import한다.
// ESM의 경우 파일 확장자를 생략할 수 없다.
import { pi, square, Person } from "./lib.mjs";

console.log(pi); // 3.141592653589793
console.log(square(10)); // 100
console.log(new Person("Lee")); // Person { name: 'Lee' }
モジュールは、エクスポートされた識別子名を一度に指定することなく、名前を一度にインポートできます.
このとき、インポートされた識別子は、asの後ろに指定された名前のオブジェクトにpropertyとして割り当てられる.
// 📁 app.mjs
import * as lib from "./lib.mjs";

console.log(lib.pi); // 3.141592653589793
console.log(lib.square(10)); // 100
console.log(new lib.Person("Lee")); // Person { name: 'Lee' }
モジュールからエクスポートされた識別子名を変更することでインポートすることもできます.
// 📁 app.mjs
import { pi as PI, square as sq, Person as P } from "./lib.mjs";

console.log(PI); // 3.141592653589793
console.log(sq(2)); // 4
console.log(new P("Kim")); // Person { name: 'Kim' }
モジュールが1つの値しかエクスポートしない場合はdefaultキーワードを使用します.
defaultキーワードとともにエクスポートされたモジュールは、{}カッコを必要とせずに任意の名前でインポートできます.
// 📁 app.mjs
import square from "./lib.mjs";

console.log(square(3)); // 9