JavaScript/模組
在 JavaScript 的早期,指令碼相對較小。在許多情況下,完整的函式都駐留在單個 JavaScript 檔案中。隨著時間的推移,需求和解決方案顯著增長。主要的是,經常使用的功能被轉移到單獨的檔案中。隨著這種複雜性的增長,意外副作用的危險也隨之增長,原始碼的模組化需求變得明顯。
原始的 JavaScript 語法 - 今天仍然有效 - 不知道不同指令碼檔案中編寫的原始碼之間的邊界。無論檔案組織如何,所有內容在任何地方都是已知的。以下示例顯示了兩個函式在 HTML 內部和彼此之間都是已知的。一個可以呼叫另一個。
<!DOCTYPE html>
<html>
<head>
<script>
alert("In script 1");
function function_1 () {
"use strict";
alert("In function_1");
function_2();
}
</script>
<script>
alert("In script 2");
function function_2 () {
"use strict";
alert("In function_2");
}
</script>
</head>
<body>
<button onclick="function_1()">Click</button>
</body>
</html>
當 HTML 頁面載入時,瀏覽器會讀取這兩個script部分;因此顯示了兩個alert訊息。單擊按鈕後,會呼叫function_1,它會呼叫function_2。
將指令碼轉移到外部檔案並透過<script src="./function_1.js"></script>引用它們時,也會出現相同的行為。
為了避免從一個函式到另一個函式或從一個檔案到另一個檔案出現意外副作用的可能性,已經開發了多種形式的模組化。
自 ECMAScript 2015 (ES6) 以來,標準定義了模組及其行為的語法。大多數瀏覽器都原生支援它,無需任何額外的庫。
關於 HTML,語法略有變化。<script>元素必須透過型別屬性<script type="module">擴充套件。這宣告一個檔案或內聯指令碼是一個模組。之後,它的內部類、函式、變數……不再對其他內聯指令碼、檔案或 HTML 可見。在以下示例中,單擊按鈕會導致錯誤訊息,因為帶有其函式function_1的內聯指令碼被視為一個模組。
<!DOCTYPE html>
<html>
<head>
<script type="module">
alert("In script 1");
function function_1 () {
"use strict";
alert("In function_1");
}
</script>
</head>
<body>
<button onclick="function_1()">Click</button>
</body>
</html>
由於安全原因,使用純內聯指令碼建立示例很複雜。我們使用帶有外部檔案的示例。在測試時,請按以下內聯指令碼所示建立此檔案。
- 當 HTML 頁面載入時,它會顯示一條警報訊息(第 7 行)。
- 外部檔案function_1.js將它的函式function_1釋出到公共(第 16 行)。所有其他功能都保持隱藏(在這個簡單的示例中,沒有其他功能)。
- 函式function_1被匯入到內聯指令碼中(第 5 行)。
- 我們透過
addEventListener將事件偵聽器新增到按鈕(第 10 行)。事件偵聽器包含一個匿名函式,該函式呼叫function_1(在外部檔案中)。在第 10 行,事件偵聽器僅宣告;此時,它不會被呼叫。 - "use script" 語句變得多餘,因為模組始終以嚴格模式執行。
<!DOCTYPE html>
<html>
<head>
<script type="module">
import {function_1} from "./function_1.js";
alert("In script 1");
const btn1 = document.getElementById("btn1");
btn1.addEventListener("click", () => function_1());
/* create a file 'function_1.js' with the following content:
function function_1() {
alert("In function_1");
}
export { function_1 };
*/
</script>
</head>
<body>
<button id="btn1">Click</button>
</body>
</html>
本質上,ES6 模組語法由兩個語句export和import組成。export用於釋出模組,使其部分類、函式或變數公開可用。import用於呼叫指令碼,以訪問這些物件。
Web 伺服器不受 HTML 元素的控制。因此,它們過去使用不同的技術來決定哪些 JS 指令碼應該被視為模組,哪些不應該。
流行的 Web 伺服器node.js支援ES 模組的export/import語法。但它的預設模組系統不同。它被稱為CommonJS。
要告訴node.js使用哪種語法,必須在專案的package.json檔案中新增一行。
{
..
"type": "module",
..
導致ES 模組語法。另一種方法是使用 '.mjs' 副檔名代替檔名的 '.js'。"type": "commonjs"行(或無定義)會導致 node.js 特定的語法CommonJS。
在CommonJS中,匯出使用modules.exports(請注意額外的 's')完成,匯入使用require語句完成。一個例子
// export in a file 'logger.js'
...
function doLogging() { ... };
module.exports = {doLogging};
// import in a file 'main.js'
const doLogging = require('./logger.js')
...