簡述JavaScript模塊化編程(二)

前置閱讀:簡述JavaScript模塊化(一)

在前面一文中员萍,我們對前端模塊化所經(jīng)歷的三個階段進行了了解:

  1. CommonJs,由于是同步的哆窿,所以主要應(yīng)用于服務(wù)器端囚聚,以Node.js為代表鸦难。
  2. AMD,異步模塊定義片林,預(yù)加載端盆,推薦依賴前置。以require.js為代表拇厢。
  3. CMD,通用模塊加載晒喷,懶加載孝偎,推薦依賴就近。以Sea.js為代表凉敲。

而在ES6已經(jīng)大行其道的今天衣盾,ES6中所提供的模塊化的方法也自然而然成了我們進行JavaScript模塊化編程的標(biāo)準(zhǔn),因此ES6模塊的語法雖然在一些較為老的瀏覽器上不能直出爷抓,需要進行轉(zhuǎn)譯势决,但卻代表著未來的JavaScript發(fā)展趨勢。

ES6模塊特性

在ES6中將模塊認(rèn)為是自動運行在嚴(yán)格模式下并且沒有辦法退出運行的JavaScript代碼蓝撇。在一個模塊中定義的變量不會自動被添加到全局共享的作用域之中果复,這個變量只能作用在這個作用域中。此外模塊還必須導(dǎo)出一些外部文件可以訪問的元素渤昌,以供其他模塊或代碼使用虽抄。

除了這個基本特性,ES6模塊還有兩大特性也十分重要独柑,需要額外注意:

  • 首先是在模塊的頂部this值是undefined迈窟,這是由于在ES6中的模塊的代碼是在嚴(yán)格模式下執(zhí)行的。(如果對this不是很熟悉的可以去看我的這篇文章:深入淺出this關(guān)鍵字
  • 其次忌栅,模塊不支持HTML風(fēng)格的代碼注釋车酣,這是早期瀏覽器所遺留下的JavaScript特性,在ES6的語法里不予支持索绪。

基本用法-模塊加載

首先我們來看瀏覽器是如何加載模塊的湖员。其實在ES6規(guī)范出來之前,web瀏覽器就規(guī)定了三種方式來引入JavaScript文件:

  • 在沒有src屬性的<script>元素中直接內(nèi)嵌JavaScript代碼
  • 在<script>元素中通過src屬性指定一個地址來加載JavaScript代碼文件
  • 通過Web Worker或Service Worker的方法加載并執(zhí)行JavaScript代碼

而在瀏覽器中瑞驱,默認(rèn)的行為就是將JavaScript作為腳本來進行加載破衔,而非模塊。所以我們要告訴瀏覽器我們加載的是模塊钱烟,方法就是在<script>元素中晰筛,將type屬性指定為"module"嫡丙。具體看下面的示例:

// 第一種方式
<script type=""module>
    import { add } from "./example";
    let num = add(1, 1);
</script>
//  第二種方式
<script type="module" src="example.js">
// 第三種方式,以腳本的方式加載example.js
let worker = new Worker("example.js");

當(dāng)HTML解析器遇到<script>元素的type="module"的時候读第,模塊文件就開始下載曙博,直到文件被完全解析完成才會去執(zhí)行模塊內(nèi)的代碼。模塊文件是按照他們出現(xiàn)在HTML文件中順序執(zhí)行的怜瞒,也就是說無論用何種方式引入模塊父泳,第一個<script type=""module>總是在第二個<script type=""module>之前執(zhí)行。

基本用法-導(dǎo)出

在ES6中我們可以使用export關(guān)鍵字將一部分代碼暴露給其他模塊吴汪,以供其他模塊或代碼使用惠窄。先讓我們來看看export關(guān)鍵字在MDN的定義吧:

export語句用于在創(chuàng)建JavaScript模塊時,從模塊中導(dǎo)出函數(shù)漾橙、對象或原始值杆融,以便其他程序可以通過 import 語句使用它們。(
此特性目前僅在 Safari 和 Chrome 原生實現(xiàn)霜运。它在許多轉(zhuǎn)換器中實現(xiàn)脾歇,如Traceur Compiler,Babel或Rollup淘捡。)

通過MDN的定義我們可以知道:export關(guān)鍵字可以將其放在任何函數(shù)藕各、對象或原始值前面,從而將它們從模塊中導(dǎo)出焦除。示例如下:

//   ./example.js
// 導(dǎo)出變量
export var a = 1;
// 導(dǎo)出函數(shù)
export function addA(value) {
    return value + a;
}
//導(dǎo)出類
export class add1 {
    constructor(value) {
        this.value = value + a;
    }
}
//這個函數(shù)就是這個模塊所私有的激况,在外部不能訪問它
function say1() {
    console.log('我是不是很帥');
}
//這又是個函數(shù)
function say2() {
    console.log('沒錯我就是很帥');
}
//在后面對函數(shù)進行導(dǎo)出,它就不是私有的了
export say2;

需要注意的是:使用export導(dǎo)出的函數(shù)和類都需要一個名稱,除非使用default關(guān)鍵字膘魄,否則就不能用這個方法導(dǎo)出匿名函數(shù)或類誉碴。所以當(dāng)我們需要導(dǎo)出匿名的函數(shù)或者類時,我們可以這么做:

//   ./example.js
//導(dǎo)出匿名函數(shù)
export default function(a, b) {
    return a + b瓣距;
}
//或者導(dǎo)出匿名的類
export default class {
consturctor(value) {
    this.value = value + 1;
    }
}

具體關(guān)于default關(guān)鍵字的用法我會在后面做具體介紹黔帕,現(xiàn)在只需記住:當(dāng)我們需要導(dǎo)出匿名的函數(shù)或者類時要使用export default語法蹈丸。

基本語法-導(dǎo)入

在ES6中成黄,從模塊中導(dǎo)入的功能可以通過import關(guān)鍵字。import語句由兩部分組成:要導(dǎo)入元素的標(biāo)識符和元素應(yīng)當(dāng)從哪個模塊導(dǎo)入逻杖。

//  ./say.js
import { say2 } from "./example.js";
console.log(say2()); // '沒錯我就是很帥'

import 后面的大括號中的say2表示從規(guī)定模塊導(dǎo)入的元素的名稱奋岁。關(guān)鍵字from后面的字符串則表示要導(dǎo)入的模塊的路徑,這通常是包含模塊的.js文件的相對或絕對路徑名荸百,需要注意的是只允許使用單引號和雙引號的字符串來包裹路徑闻伶,瀏覽器使用的路徑格式與傳給<script>元素的相同,所以必須把文件的擴展名也加上够话。

(注:由于Node.js遵循基于文件系統(tǒng)前綴以區(qū)分本地文件個包的慣例蓝翰,即example是一個包光绕,而./exampple.js是一個本地文件。為了更好的兼容多個瀏覽器Node.js環(huán)境畜份,我們一定要在路徑前包含./或../來表示要導(dǎo)入的文件诞帐。)

除此之外,我們還可以導(dǎo)入多個元素或者直接導(dǎo)入整個模塊:

// 導(dǎo)入多個元素
improt { a, addA, say2 } from "./example.js";
console.log(a); // 1
sonsole.log(addA(1); // 2

// 導(dǎo)入整個模塊
import * as example from "./example.js"
console.log(example.a); // 1
sonsole.log(example.addA(1); // 2
console.log(example.say2()); // '沒錯我就是很帥'

上面的導(dǎo)入整個模塊就是把example.js中導(dǎo)出的所有元素全部加載到一個叫做example的對象中爆雹,而所導(dǎo)出的元素就會作為example的屬性被訪問停蕉。因為example對象是作為example.js中所導(dǎo)出成員的命名空間對象而被創(chuàng)建的,所以這種導(dǎo)入方式被稱為命名空間導(dǎo)入(name space import)钙态。

還有一點要注意的是慧起,不管import語句把一個模塊寫了多少次,該模塊只執(zhí)行一次册倒。意思就是蚓挤,在首次執(zhí)行導(dǎo)入模塊后,實例化的模塊就會被保存在內(nèi)存中剩失,只要使用import語句引用它就可以重復(fù)使用它:

// 首次導(dǎo)入需要加載模塊example.js
import { a } from "./example.js"
// 下面的兩個import將無需加載example.js了
import { addA } from "./example.js"
import { say2 } from "./example.js"

當(dāng)從模塊中導(dǎo)入一個元素時屈尼,它與const是一樣無法定義另一個同名變量和導(dǎo)入一個同名元素册着,也無法在import語句前使用元素或者改變導(dǎo)出的元素的值:

//接上面的代碼

say2 = 1 ;  //會拋出一個錯誤

這是由于ES6的import語句為導(dǎo)入的元素創(chuàng)建的是只讀綁定的標(biāo)識符拴孤,而不是原始綁定。因此元素只有在被導(dǎo)出的模塊中才可以被修改甲捏,即使是將該模塊的全部導(dǎo)入也無法修改其中的元素演熟。

//   ./example.js
// 這是一個函數(shù)
export function setA(newA) {
    a = newA;
}
//  ./say.js
import { a, setA } from "./example";
console.log(a);  // 1
a = 2;   //拋出錯誤

// 所以我們得這么做
setA(2); 
console.log(a);  // 2

調(diào)用setA(2)時會返回到example.js中去執(zhí)行,將a設(shè)置為2司顿。由于say.js導(dǎo)入的只是a的只讀綁定的標(biāo)識符而已芒粹,因此會自動進行更改。

其他基本語法

1.語法限制

export和import在語法上還有一個重要的限制大溜,那就是他們必須在條件語句和函數(shù)之外使用化漆,例如:

if (ture) {
    export var a = 1;      //語法錯誤
}
function imp() {
    import a from "./example.js"; //語法錯誤
}

由于模塊語法存在的其中一個原因是讓JavaScript引擎可以靜態(tài)地確定哪些代碼是可以導(dǎo)出的,因此export和import語句被設(shè)計成靜態(tài)的钦奋,不能進行任何形式的動態(tài)導(dǎo)出或?qū)搿?/p>

2.重命名解決

有時在開發(fā)中座云,我們在導(dǎo)入一些元素后不想使用它們的原始名稱了,我們就可以在導(dǎo)出過程或者導(dǎo)入過程中去改變導(dǎo)出元素的名稱:

// 導(dǎo)出過程
function add(a, b) {
    return a + b;
}
export { add as add1 };  //在導(dǎo)入過程中必須使用add1作為名稱 

// 導(dǎo)入過程
import {add as add1 } from "./example"
console.log(add1(1,1));  // 2
console.log(typeof add); //undefined
3.模塊的默認(rèn)值

在CommonJS等其他的模塊化規(guī)范中付材,從模塊中導(dǎo)出或?qū)肽J(rèn)值是一個常見的用法朦拖,因此在ES6中也延用了這種用法并進行了優(yōu)化。在ES6中我們可以使用default關(guān)鍵字來指定默認(rèn)值厌衔,并且一個模塊只能默認(rèn)一個導(dǎo)出值:

// ./example.js
// 第一種默認(rèn)導(dǎo)出語法
export default function(a, b) {
    return a + b;
}
// 第二種默認(rèn)導(dǎo)出語法
function add(a, b) {
    return a + b;
}
export default add;
// 第三種默認(rèn)導(dǎo)出語法
function add(a, b) {
    return a + b;
}
export { add as default };

需要注意的是第三種語法璧帝,default關(guān)鍵字雖然不能作為元素的名稱,但可以作為元素的屬性名稱富寿,因此可以使用as語法將add函數(shù)的屬性設(shè)置為default睬隶。

導(dǎo)入默認(rèn)值的語法則是這樣的:

//  第一種語法
import add from "./example";
//  第二種語法
import { default as add } from "./example";

看到這里有些朋友可能會發(fā)現(xiàn)锣夹,我們的第一種語法中import關(guān)鍵字后面并沒有加大括號,認(rèn)為這是錯誤的理疙。其實這是導(dǎo)入默認(rèn)值的獨特語法晕城,在這的本地名稱add用于表示模塊導(dǎo)出的任何默認(rèn)函數(shù),這種語法是最純凈的窖贤,ES6標(biāo)準(zhǔn)創(chuàng)建團隊的大佬們也希望這種語法能成為web主流的模塊導(dǎo)入形式砖顷。

我們前面說的導(dǎo)入匿名函數(shù)也同樣使用這種語法:

//   ./example.js
//導(dǎo)出匿名函數(shù)
export default function(a, b) {
    return a + b;
}
// ./say.js
import add from "./example";
console.log(add(1,1));  // 2

在這里本地名稱add就是用于表示上面的匿名函數(shù)的赃梧。

4.導(dǎo)出已導(dǎo)入的元素

我們同樣可以在本模塊內(nèi)導(dǎo)出我們在本模塊內(nèi)導(dǎo)入的元素滤蝠,有以下幾種語法:

//  第一種語法
import { add } from ./example.js;
export { add };

//  第二種語法
export { add } from ./example.js;

//換一個名稱導(dǎo)出
export { add as add1 } from ./example.js; //以add這個名稱導(dǎo)入,再以add1的名稱導(dǎo)出

// 導(dǎo)出整個模塊
export *  from ./example.js;

// 最后求一波暑期前端實習(xí)的坑位-_-||

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末授嘀,一起剝皮案震驚了整個濱河市物咳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蹄皱,老刑警劉巖览闰,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異巷折,居然都是意外死亡压鉴,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門锻拘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來油吭,“玉大人,你說我怎么就攤上這事署拟⊥裨祝” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵推穷,是天一觀的道長心包。 經(jīng)常有香客問我,道長馒铃,這世上最難降的妖魔是什么蟹腾? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮骗露,結(jié)果婚禮上岭佳,老公的妹妹穿的比我還像新娘。我一直安慰自己萧锉,他們只是感情好珊随,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般叶洞。 火紅的嫁衣襯著肌膚如雪鲫凶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天衩辟,我揣著相機與錄音螟炫,去河邊找鬼。 笑死艺晴,一個胖子當(dāng)著我的面吹牛昼钻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播封寞,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼然评,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了狈究?” 一聲冷哼從身側(cè)響起碗淌,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎抖锥,沒想到半個月后亿眠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡磅废,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年纳像,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片还蹲。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡爹耗,死狀恐怖耙考,靈堂內(nèi)的尸體忽然破棺而出谜喊,到底是詐尸還是另有隱情,我是刑警寧澤倦始,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布斗遏,位于F島的核電站,受9級特大地震影響鞋邑,放射性物質(zhì)發(fā)生泄漏诵次。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一枚碗、第九天 我趴在偏房一處隱蔽的房頂上張望逾一。 院中可真熱鬧,春花似錦肮雨、人聲如沸遵堵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陌宿。三九已至锡足,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間壳坪,已是汗流浹背舶得。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留爽蝴,地道東北人沐批。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像蝎亚,于是被迫代替她去往敵國和親珠插。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持颖对,譯者再次奉上一點點福利:阿里云產(chǎn)品券捻撑,享受所有官網(wǎng)優(yōu)惠,并抽取幸運大...
    HetfieldJoe閱讀 3,657評論 2 27
  • 模塊通常是指編程語言所提供的代碼組織機制缤底,利用此機制可將程序拆解為獨立且通用的代碼單元顾患。所謂模塊化主要是解決代碼分...
    MapleLeafFall閱讀 1,170評論 0 0
  • 1、Doctype作用个唧?標(biāo)準(zhǔn)模式與兼容模式各有什么區(qū)別? 1江解、聲明位于位于HTML文檔中的第一行,處于 標(biāo)簽之前...
    CRUD_科科閱讀 484評論 0 6
  • 每次都會情不自禁地問為什么這么蠢徙歼!說話不經(jīng)過大腦焉能不蠢犁河?還何談批判性思維,想當(dāng)然耳就足矣魄梯。 意識到這一點后桨螺,雖時...
    蟲鳴吹晚風(fēng)閱讀 374評論 0 0
  • 燕子:時間一晃,你也邁入而立之年了酿秸,過上上有老下有小的生灭翔。要關(guān)注家里這個月的生活費留夠沒?孩子的學(xué)業(yè)有長進沒辣苏?老人...
    景心9981閱讀 72評論 0 1