詳述JavaScript的官方模塊風格。加入這一定義旨在代替過去幾年中出現過的許多非正式的模塊定義風格。 ...
什麼是模塊
模塊是自動運行在嚴格模式下並且沒有辦法退出運行的 JavaScript 代碼。
在模塊頂部創建的變數不會自動被添加到全局變數作用域,這個變數僅在模塊的頂級作用域中存在,而且模塊必須導出一些外部代碼可以訪問的元素。
模塊也可以從其他模塊導入綁定。
在模塊的頂部,this 的值是 undefined。
模塊不支持 HTML 風格的代碼註釋。
導出的基本語法
用 export 關鍵字將一部分已發佈的代碼暴露給其它模塊。
// 導出數據
export var color = "red";
export let name = "JiaJia";
export const magicNumber = 7
// 導出函數
export function sum(num1, num2) {
return num1 + num2;
}
// 導出類
export class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
}
// this is private function
function subtract(num1, num2) {
return num1 - num2;
}
// define a function
function multiply(num1, num2) {
return num1 * num2;
}
// export it
export multiply;
導入的基本語法
通過 import 關鍵字導入在另一個模塊中導出的功能。
import { identifier1, identifier2 } from "./example.js"
import 後面的大括弧表示從給定模塊導入的綁定(binding),關鍵字 from 表示從哪個模塊導入給定的綁定。
當從模塊中導入一個綁定時,它就好像使用 const 定義的一樣。結果是你無法定義另一個同名變數(包括導入另一個同名綁定),也無法在 import 語句前使用標識符或改變綁定的值。
導入單個綁定
import { sum } from "./example.js";
console.log(sum(1, 2)); // 3
// 拋出錯誤
sum = 1;
導入多個綁定
import { sum, multiply, magicNumber } from "./example.js"
console.log(sum(1, magicNumber)); // 8
console.log(multiply(1, 2)); // 2
導入整個模塊
// 命名空間導入
import * as example from "./example.js";
console.log(example.sum(1, example.magicNumber)); // 8
console.log(example.multiply(1, 2)); // 2
不管在 import 語句中把一個模塊導入了多少次,該模塊只執行一次。
如果同一個應用程式中的其他模塊也從 example.js 導入綁定,那麼這些模塊與此代碼將使用相同的模塊實例。
導入綁定的一個微妙怪異之處
export var name = "JiaJia";
export function setName(newName) {
name = newName;
}
import { name, setName } from "./example.js";
console.log(name); // "JiaJia"
setName("L");
console.log(name); // "L"
name = "JiaJia"; // 拋出錯誤
導出和導入時重命名
function sum(num1, num2) {
return num1 + num2;
}
export {
sum as add
};
import { add as sum } from "./example.js";
模塊的預設值
模塊的預設值指的是通過 default 關鍵字指定的單個變數、函數或類,只能為每個模塊設置一個預設的導出值。
export default function (num1, num2) {
return num1 + num2;
}
等同於
function sum (num1, num2) {
return num1 + num2;
}
export default sum;
等同於
function sum (num1, num2) {
return num1 + num2;
}
export { sum as default };
導入預設值
import sum from "./example.js";
console.log(sum(1, 2)); // 3
導入預設值和以外的值:
import sum, { color } from "./example.js";
或者
import { default as sum, color } from "./example.js";
重新導出一個綁定
import { sum } from "./example.js";
export { sum }
或者簡寫成如下形式:
export { sum } from "./example.js";
導出時也可以定義別名
export { sum as add } from "./example.js";
導出一切:
export * from "./example.js";
無綁定導入
// 沒有 export 或 import 的模塊代碼
Array.prototype.pushAll = function(items) {
if (!Array.isArray(items)) {
throw new TypeError("參數必須是一個數組");
}
return this.push(...items);
}
import "./example.js"
載入模塊
在Web瀏覽器中使用模塊
- 在
<script>
元素中通過src屬性指定一個載入代碼的地址來載入 JavaScript 代碼文件 - 將 JavaScript 代碼內嵌到沒有 src 屬性的
<script>
元素中 - 通過 Web Worker 或 Service Worker 的方法載入並執行 JavaScript 代碼文件
在<Script>
中使用模塊
<!-- 載入一個 JavaScript 模塊文件 -->
<script type="model" src="module.js"></script>
<!-- 內聯引入一個模塊 -->
<script type="model">
import { sum } from "./example.js";
let result = sum(1, 2);
</script>
Web瀏覽器中的模塊載入順序
模塊按照它們出現在 HTML 文件中的順序執行,也就是說,無論模塊中包含的是內聯代碼還是指定 src 屬性,第一個 <script type="module">
總是在第二個之前執行。
Web瀏覽器中的非同步模塊載入
當應用 async 屬性時,腳本文件將在文件完全下載並解析後執行。
腳本在文檔中的順序不會影響腳本執行的順序。
<!-- 無法保證這兩個哪個先執行 -->
<script type="module" async src="module1.js"></script>
<script type="module" async src="module2.js"></script>
將模塊作為 Worker 載入
Worker,例如 Web Worker 和 Service Worker,可以在網頁上下文之外執行 JavaScript 代碼。
// 預設按照腳本的方式載入 script.js
let worker = new Worker("script.js");
// 預設按照模塊的方式載入 script.js
let worker = new Worker("script.js", { type: "module" });
瀏覽器模塊說明符解析
瀏覽器要求模塊說明符具有以下幾種格式之一:
- 以/開頭的解析為從根目錄開始
- 以./開頭的解析為從當前目錄開始
- 以../開頭的解析為從父目錄開始
- 以URL格式
// 從 https://www.example.com/modules/example1.js 導入
import { first } from "./example1.js";
// 從 https://www.example.com/example2.js 導入
import { second } from "../example2.js";
// 從 https://www.example.com/example3.js 導入
import { third } from "./example3.js";
// 從 https://www.example.com/example4.js 導入
import { fourth } from "https://www.example.com/example4.js";