掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯(lián)網(wǎng)交流
在互聯(lián)網(wǎng)的洪荒時代,網(wǎng)站主要用 HTML和 CSS 開發(fā)的。如果將 JavaScript 加載到頁面中,通常是以小片段的形式提供效果和交互,一般會把所有的 JavaScript 代碼全都寫在一個文件中,并加載到一個 script 標簽中。盡管可以把 JavaScript 拆分為多個文件,但是所有的變量和函數(shù)仍然會被添加到全局作用域中。

但是后來 JavaScript 在瀏覽器中發(fā)揮著重要的作用,迫切需要使用第三方代碼來完成常見任務,并且需要把代碼分解為模塊化的文件,避免污染全局命名空間。
ECMAScript 2015 規(guī)范在 JavaScript 語言中引入了 module,也有了 import 和 export 語句。在本文中,我們一起來學習 JavaScript 模塊,以及怎樣用 import 和 export 來組織代碼。
在 JavaScript 中出現(xiàn)模塊的概念之前,當我們想要把自己的代碼組織為多個塊時,一般會創(chuàng)建多個文件,并且將它們鏈接為單獨的腳本。下面先舉例說明,首先創(chuàng)建一個 index.html 文件和兩個JavaScript文件“ functions.js 和 script.js。
index.html 文件用來顯示兩個數(shù)字的和、差、乘積和商,并鏈接到 script 標簽中的兩個 JavaScript 文件。打開 index.html 并添加以下代碼:
index.html
JavaScript Modules Answers
and
Addition
Subtraction
Multiplication
Division
這個頁面很簡單,就不詳細說明了。
functions.js 文件中包含將會在第二個腳本中用到的數(shù)學函數(shù)。打開文件并添加以下內(nèi)容:
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 文件用來確定 x 和 y 的值,以及調(diào)用前面那些函數(shù)并顯示結(jié)果:
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 可以看到所有結(jié)果:
對于只需要一些小腳本的網(wǎng)站,這不失為一種有效的組織代碼的方法。但是這種方法存在一些問題:
由于受限于 CORS 策略,必須在服務器環(huán)境中使用模塊,否則會出現(xiàn)下面的錯誤:
- 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.
模塊與常規(guī)腳本不一樣的地方:
模塊仍然經(jīng)常與打包程序(如 Webpack)一起配合使用,用來增加對瀏覽器的支持和附加功能,但它們也可以直接用在瀏覽器中。
接下來探索更多使用 import 和 export 語法的方式。
如前所述,使用 export 語法允許你分別導入按名稱導出的值。以這個 function.js 的簡化版本為例:
functions.js
- export function sum() {}
- export function difference() {}
這樣允許你用花括號按名稱導入 sum 和 difference:
script.js
- import {sum, difference} from './functions.js'
也可以用別名來重命名該函數(shù)。這樣可以避免在同一模塊中產(chǎn)生命名沖突。在這個例子中,sum 將重命名為 add,而 difference 將重命名為 subtract。
script.js
- import {
- sum as add,
- difference as subtract
- } from './functions.js'
- add(1, 2) // 3
在這里調(diào)用 add() 將產(chǎn)生 sum() 函數(shù)的結(jié)果。
使用 * 語法可以將整個模塊的內(nèi)容導入到一個對象中。在這種情況下,sum 和 difference 將成為 mathFunctions 對象上的方法。
script.js
- import * as mathFunctions from './functions.js'
- mathFunctions.sum(1, 2) // 3
- mathFunctions.difference(10, 3) // 7
原始值、函數(shù)表達式和定義、異步函數(shù)、類和實例化的類都可以導出,只要它們有標識符就行:
- // 原始值
- 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']
- // 函數(shù)表達式
- export const sum = (x, y) => x + y
- // 函數(shù)定義
- export function difference(x, y) {
- return x - y
- }
- // 匿名函數(shù)
- export async function getBooks() {}
- // 類
- export class Book {
- constructor(name, author) {
- this.name = name
- this.author = author
- }
- }
- // 實例化類
- export const book = new Book('Lord of the Rings', 'J. R. R. Tolkein')
所有這些導出都可以成功被導入。接下來要探討的另一種導出類型稱為默認導出。
默認導出在前面的例子中我們導出了多個命名的導出,并分別或作為一個對象導入了每個導出,將每個導出作為對象上的方法。模塊也可以用關鍵字 default 包含默認導出。默認導出不使用大括號導入,而是直接導入到命名標識符中。
以 functions.js 文件為例:
functions.js
- export default function sum(x, y) {
- return x + y
- }
在 script.js 文件中,可以用以下命令將默認函數(shù)導入為 sum:
script.js
- import difference from './functions.js'
- difference(1, 2) // 3
不過這樣做很危險,因為在導入過程中對默認導出的命名沒有做任何限制。在這個例子中,默認函數(shù)被導入為 difference,盡管它實際上是 sum 函數(shù):
script.js
- import difference from './functions.js'
- difference(1, 2) // 3
所以一般首選使用命名導出。與命名導出不同,默認導出不需要標識符——原始值本身或匿名函數(shù)都可以用作默認導出。以下是用作默認導出的對象的示例:
functions.js
- export default {
- name: 'Lord of the Rings',
- author: 'J. R. R. Tolkein',
- }
可以用以下命令將其作為 book 導入:
functions.js
- import book from './functions.js'
同樣,下面的例子演示了如何將匿名箭頭函數(shù)導出為默認導出:
functions.js
- export default () => 'This function is anonymous'
可以這樣導入:
script.js
- import anonymousFunction from './functions.js'
命名導出和默認導出可以彼此并用,例如在這個模塊中,導出兩個命名值和一個默認值:
functions.js
- export const length = 10
- export const width = 5
- export default function perimeter(x, y) {
- return 2 * (x + y)
- }
可以用以下命令導入這些變量和默認函數(shù):
script.js
- import calculatePerimeter, {length, width} from './functions.js'
- calculatePerimeter(length, width) // 30
現(xiàn)在默認值和命名值都可用于腳本了。
模塊化編程設計允許我們把代碼分成單個組件,這有助于代碼重用,同時還可以保護全局命名空間。一個模塊接口可以在原生 JavaScript 中用關鍵字 import 和 export 來實現(xiàn)。

我們在微信上24小時期待你的聲音
解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯(lián)網(wǎng)交流