ES6之路之模塊詳解

歡迎訪問我的站點javascript

簡介

何爲模塊

一個模塊只不過是一個寫在文件中的 JavaScript 代碼塊。html

模塊中的函數或變量不可用,除非模塊文件導出它們。前端

簡單地說,這些模塊能夠幫助你在你的模塊中編寫代碼,而且只公開應該被你的代碼的其餘部分訪問的代碼部分。java

爲何要使用模塊

  1. 增長可維護性:因爲每一個模塊都是獨立的,每一個人寫的代碼是不會相互影響的,在維護代碼的時候很好排查是哪一個模塊出錯。
  2. 可複用性:在平常的開發中,特別是大點的項目,代碼的可複用性就更重要了,也許你會用複製粘貼的形式,可是直接一個 import 命令就能夠搞定,豈不快哉。
  3. 避免命名污染:在 javascript 腳本中,全部的 js 文件的頂級做用域建立的變量,會被添加到共享的全局做用域,這就會致使不一樣的人開發的代碼可能會有相同的變量名,致使變量名污染。

如何使用

導出模塊

導出模塊所用的命令是 export。chrome

前面也提到一個模塊就是一個 javascript 文件,在這個模塊中定義的變量,外部是沒法獲取到的,只有經過 export 導出的變量其餘模塊才能夠用瀏覽器

最簡單的導出方式就是在聲明的變量、函數、類前面加一個 exportecmascript

// export1.js 

// 導出變量
export let name = '桃翁';

// 導出函數
export function print() {
    console.log("歡迎關注公衆號:前端桃園");
}

// 導出類
export class Person {
    constructor(name) {
        this.name = name;
    }
}
// 私有函數
function privateFunction () {
    console.log('我是私有函數,外部訪問不了我');
}
複製代碼

注意: 1. 被導出的函數或者類,都必需要有名稱,意思就是說不能用這種方式導出匿名函數或者匿名類。 2. privateFunction 函數,沒有加 export 命令,被當作這個模塊的私有變量,其餘模塊是訪問不到的。函數

除了上面那種導出方式,還有另一種測試

// export2.js

// 導出變量
let name = '桃翁'// 導出函數
function print() {
    return '歡迎關注公衆號:前端桃園';
}

// 導出類
class Person {
    constructor(name) {
        this.name = name;
    }
}

// 私有函數
function privateFunction () {
    return '我是私有函數,外部訪問不了我';
}

export { name, print, Person }
複製代碼

上面這種寫法導入一組變量,與 export1.js 是等價的。網站

導入模塊

導入的模塊能夠理解爲是生產者(或者服務的提供者),而使用導入的模塊的模塊就是消費者。

導入模塊的命令是 import, import 的基本形式以下:

import { var1, var2 } from './example.js'
複製代碼

import 語句包含兩部分:一是導入須要的標識符,二是模塊的來源。

注意:瀏覽器中模塊來源要以「/」或者 「./」 或者 「../」開頭 或者 url 形式,否則會報錯。

例如咱們導入 export1.js 模塊,能夠這麼導入

// import1.js
import { name, print, Person } from './export1.js';

console.log(name); // 桃翁

console.log(print()); // 歡迎關注公衆號:前端桃園

// 報錯, 不能定義相同名字變量
let name = 2333; 

// 報錯,不能從新賦值
name = "小豬";
複製代碼

能夠看到導入綁定(這裏不理解綁定,文章後面會解釋)時,形式相似於對象解構,但實際上並沒有關聯。

當導入綁定的時候,綁定相似於使用了 const 定義,意味着不能定義相同的變量名,可是沒有暫時性死區特性(可是在 深刻理解ES6 這本書裏面說是有暫時性死區限制,我在 chrome 上測試了的,讀者但願也去試下,到底受不受限制)。

let name = 2333;
複製代碼

上面這行代碼會報錯。

命名空間導入

這種導入方式是把整個生產者模塊當作單一對象導入,全部的導出被當作對象的屬性。

// import2.js
import * as namespace from './export1.js'

console.log(namespace.name); // 桃翁

console.log(namespace.print()); // 歡迎關注公衆號:前端桃園
複製代碼

重命名導入導出

有時候你並不想導出變量的原名稱,須要從新命名,這個時候只須要使用 as 關鍵字來制定新的名字便可。

重命名導出

// export3.js

function print() {
    return '歡迎關注公衆號:前端桃園';
}

export { print as advertising }
複製代碼

導重命名入

拿上面導出的舉例子

// import3.js
import { advertising as print } from './export3.js'

console.log(typeof advertising); // "undefined"

console.log(print()); // 歡迎關注公衆號:前端桃園 
複製代碼

此代碼導入 advertising 函數並重命名爲了 print ,這意味着此模塊中 advertising 標識符不存在了。

default 關鍵字

default 關鍵字是用來作默認導入導出的。

默認導出

// defaultExport.js

// 第一種默認導出方式
export default function print() {
    return '歡迎關注公衆號:前端桃園';
}

// 第二種默認導出方式
function print() {
    return '歡迎關注公衆號:前端桃園';
}

export default print;

// 第三種默認導出方式
function print() {
    return '歡迎關注公衆號:前端桃園';
}

export { print as default }
複製代碼

default 這個關鍵字在 JS 中具備特殊含義,既能夠做爲同命名導出,又標明瞭模塊須要使用默認值。

注意: 一個模塊中只能有一個默認導出。

默認導入

默認導入和通常的導入不一樣之處就是不須要寫大括號了,看起來更簡潔。

把上面 defaultExport.js 模塊導出的做爲例子

import print from './defaultExport.js'

console.log(print()); // 歡迎關注公衆號:前端桃園 
複製代碼

那若是既有默認的又有非默認的怎麼導入呢?看例子就明白了

// defaultImport1.js

let name = '桃翁';

function print() {
    return '歡迎關注公衆號:前端桃園';
}

export { name, print as default }

複製代碼
// defaultImport2.js

import print, { name } from './defaultImport1.js'

console.log(print()); // 歡迎關注公衆號:前端桃園

console.log(name); // 桃翁
複製代碼

混合導入須要把默認導入的名稱放在最前面,而後用逗號和後面非默認導出的分割開。

思考了好久是否應該加上進階內容,原本是想寫入門級系列的,可是想了想,仍是都寫進來吧,入門的看入門前面基礎,深刻理解的看進階。

進階

進階部分主要介紹 模塊的幾個特性

  • 靜態執行
  • 動態關聯
  • 模塊不會重複執行

靜態執行

所謂靜態執行其實就是在編譯階段就須要肯定模塊的依賴關係,那麼就會出現 import 命令會優先於模塊其餘內容的執行,會提早到編譯階段執行。

// static1.js
console.log('佩奇');

import { nouse } from './static2.js'

// static2.js
export function nouse() {
    return '我是不須要的';
}

console.log('小豬');
複製代碼

能夠看到最後輸出的應該是「小豬」先輸出,而「佩奇」後輸出,能夠得出雖然 static2.js 在後面引入,可是會被提高到模塊的最前面先執行。

這也是我前面所說的不受暫時性死區緣由之一,在這裏能夠寫一個例子試試:

// static3.js
console.log(nouse());

import { nouse } from './static2.js'

// 結果:
// 小豬
// 我是不須要的
複製代碼

經檢驗確實是能夠在 import 以前使用導入的綁定。

靜態執行還會致使一個問題,那就是不能動態導入模塊。

// 報錯
if (flag) {
    import { nouse } from './static3.js'
}

// 報錯
import { 'no' + 'use' } from './static3.js'

複製代碼

由於 import 是靜態執行的,因此在靜態(詞法)分析階段,是無法獲得表達式或者變量的值的。

可是爲了解決這個問題,由於了 import() 這個函數,這個算擴展內容吧,寫太多了我怕沒人看完了,後面會有擴展閱讀連接。

動態關聯

所謂的動態關聯,其實就是一種綁定關係, 這是 ES6 很是重要的特性,必定仔細閱讀。

在 ES6 的模塊中,輸出的不是對象的拷貝,不論是引用類型仍是基本類型, 都是動態關聯模塊中的值,。

// dynamic1.js
export let name = '桃翁';

export function setName(newName) {
    name = newName;
}

// dynamic2.js
import { name, setName } from './dynamic1.js'

console.log(name); // 桃翁

setName('不要臉');

console.log(name); // 不要臉
複製代碼

奇蹟般的發如今 dynamic2.js 模塊中能夠修改 dynamic1.js 模塊裏面的值, 而且反應到 name 綁定上(這個是重點,這個反應到了消費者模塊), 因此咱們把導入的變量叫作綁定。

在生產者模塊導出的變量與消費者模塊導入的變量會有一個綁定關係,不管前者或者後者發生改變,都會互相影響。

注意區分在一個文件或模塊中基本類型的賦值,二者是互不影響的。

模塊不會重複執行

這個特性比較好理解,就是若是從一個生產者模塊中分別導入綁定,而不是一次性導入,生產者模塊不會執行屢次。

// noRepeat1.js
export let name = '桃翁';

export let age = '22';

console.log('我正在執行。。。');

// noRepeat2.js
import { name } from './noRepeat1.js';
import { age } from './noRepeat1.js';

console.log(name);
console.log(age);

// 結果
// 我正在執行。。。
// 桃翁
// 22
複製代碼

雖然導入了兩次,可是 noRepeat1.js 只有執行一次。若同一個應用(注意是同一個應用不是模塊)中導入同一個模塊,則那些模塊都會使用一個模塊實例,意思就是說是一個單例。

後記

碼字不易,寫技術文章是真的累,做者花的時間至少是讀者讀的時間的十倍。在此想到阮老師寫了那麼多文章,不知道是花了多少時間,居然還有人這麼恨他,攻擊他的網站。

我在文章中給我公衆號打了不少廣告,在此抱個歉,剛運營的公衆號,須要拉點粉絲,不喜歡的注重內容就好。

拓展

原生ECMAScript模塊: 動態 import()

相關文章
相關標籤/搜索