ES6 Module
Mia Yang
The single most important code organization pattern
in all of JavaScript is Module
# Traditional Module Pattern
// multiple
function traditional (name) {
function greeting () {
console.log("Hello " + name + "!");
}
return {
greeting
};
}
const Mia = traditional("Mia");
Mia.greeting()
// singleton
const Ian = (function (name) {
function greeting () {
console.log("Hello " + name + "!");
}
return { greeting }
})("Ian")
Ian.greeting()
# Moving Forward
-
File-Based Module (one module one file)
-
Static (better than CommonJS)
-
Singletons (reference to the one centralized instance)
-
Properties and methods are actual binding to the identifiers in the inner module definition
# CommonJS
Node.js 中的 Module 載入機制是,呼叫 require 時檢查載入的模組是否在快取中,如果在,則直接返回該 Module 的內容。如果不在,則建立該 Module 的 Instance,並將它儲存到快取中,以便下次require 使用。 這也意味著每次 require 時,都會得到相同的 Instance。
# export
// Way 1
export let firstName = 'Ian';
export let lastName = 'Huang';
export let year = '1997';
// Way 2
let firstName = 'Mia';
let lastName = 'Yang';
let year = 1998;
let greeting = function () {
console.log(`Hi, I'm ${firstName}`)
}
// Also can rename module memebr during named export
export { firstName, lastName, year, greeting as greetingFun }
# import
// module.js
let firstName = 'Mia';
let lastName = 'Yang';
let year = 1998;
let greeting = function () {
console.log(`Hi, I'm ${firstName}`)
}
export { firstName, lastName, year, greeting }
// main.js
import { firstName, lastName, year, greeting as greetingFun } from './module.js'
console.log(firstName, lastName, year)
greetingFun()
// -------- OUTPUT ---------
// Mia Yang 1998
// Hi I'm Mia
# Immutable / Read-only
import {a} from './xxx.js'
a = {}; // Syntax Error : 'a' is read-only;
// But, if a is object
import {a} from './xxx.js'
a.foo = 'hello'; // 合法操作
# Hoisting
foo();
import { foo } from 'my_module';
由於 import 是靜態分析(載入),所以在編譯階段就會先執行的,故有 Hoisting 的效果,也因為是靜態執行,所以 expression、variable、if condition 都是沒辦法在編譯階段得到實質的
// 报错
import { 'f' + 'oo' } from 'my_module';
// 报错
let module = 'my_module';
import { foo } from module;
// 报错
if (x === 1) {
import { foo } from 'module1';
} else {
import { foo } from 'module2';
}
# Entirely Import (*)
// main.js
// individually import
import { area, circumference } from './circle';
console.log('圆面积:' + area(4));
console.log('圆周长:' + circumference(14));
// Entirely import
import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));
// circle.js
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}
# export default
// module.js
// way 1
export default function () {
console.log("Hi I'm Mia")
}
// way2
function greeting () { /***/ }
export default greeting
// way3
function greeting () { /***/ }
export { greeting as default, sss}
// main.js
import greeting, { sss } from "./module.js"
greeting()
// ------ OUTPUT ------
// Hi I'm Mia
# Difference
// module.js
let greeting = 'Hi, Mia'
export default greeting
setTimeout(() => {
greeting = 'Hi, Ian'
}, 1000)
// main.js
import greeting from './module.js'
console.log(greeting)
setTimeout(() => {
console.log(greeting)
}, 3000)
// ------ OUTPUT -------
// Hi, Mia
// HI, Mia
// module.js
let greeting = 'Hi, Mia'
export { greeting as default }
setTimeout(() => {
greeting = 'Hi, Ian'
}, 1000)
// main.js
import greeting from './module.js'
console.log(greeting)
setTimeout(() => {
console.log(greeting)
}, 3000)
// ------ OUTPUT -------
// Hi, Mia
// HI, Ian
# import()
前面有提到說 export 和 import 為靜態分析(載入),這樣的設計雖有利於編輯器的效率,但也導致無法做到 require 一樣地條件載入,所以在 ES2020 中加入了 import()來支持動態的條件載入!
// case1
button.addEventListener('click', event => {
import('./dialogBox.js')
.then(dialogBox => {
dialogBox.open();
})
.catch(error => {
})
});
// case2
if (condition) {
import('moduleA').then(...);
} else {
import('moduleB').then(...);
}
# Browser Loading
默認預設的情況下,瀏覽器是同步加載 JS Script,這表示瀏覽器遇到 <script> 時會停下來載入並執行,再繼續渲染,如果是外部的 Script 還必須等下載的時間,如果 Script 檔案太大也有可能造成 UI Blocking。
所以瀏覽器後來有提供 defer 與 async 屬性,來支持異步加載。
<!-- 页面内嵌的脚本 -->
<script type="application/javascript">
// module code
</script>
<!-- 外部脚本 -->
<script type="application/javascript" src="path/to/myModule.js">
</script>
<script src="path/to/myModule.js" defer></script>
<script src="path/to/myModule.js" async></script>
# defer v.s async & type module
瀏覽器遇到這兩個屬性,就會開始載入腳本,但不會等它下載和執行,而兩者主要的區別是
-
defer: 渲染完就執行,如果有多個 defer 就會依續執行
-
async: 載入/下載完就執行,如果有多個 async 也不能保證順序
在 ES6 之後,新增了 type="module" 屬性
<script type="module" src="./foo.js"></script>
<!-- 等同於 -->
<script src="./foo.js" defer></script>
<!-- 當然也可以使用 async -->
<script type="module" src="./foo.js" async></script>
You Don't Know JS - ES6 Module
By miayang0513
You Don't Know JS - ES6 Module
- 389