簡述JavaScript模塊化編程(二)

来源:https://www.cnblogs.com/mingdao/archive/2019/08/02/11290240.html
-Advertisement-
Play Games

前置閱讀:簡述JavaScript模塊化(一) 在前面一文中,我們對前端模塊化所經歷的三個階段進行了瞭解: CommonJs,由於是同步的,所以主要應用於伺服器端,以Node.js為代表。 AMD,非同步模塊定義,預載入,推薦依賴前置。以require.js為代表。 CMD,通用模塊載入,懶載入,推薦 ...


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

在前面一文中,我們對前端模塊化所經歷的三個階段進行了瞭解:

  1. CommonJs,由於是同步的,所以主要應用於伺服器端,以Node.js為代表。
  2. AMD,非同步模塊定義,預載入,推薦依賴前置。以require.js為代表。
  3. CMD,通用模塊載入,懶載入,推薦依賴就近。以Sea.js為代表。

而在ES6已經大行其道的今天,ES6中所提供的模塊化的方法也自然而然成了我們進行JavaScript模塊化編程的標準,因此ES6模塊的語法雖然在一些較為老的瀏覽器上不能直出,需要進行轉譯,但卻代表著未來的JavaScript發展趨勢。

ES6模塊特性

在ES6中將模塊認為是自動運行在嚴格模式下並且沒有辦法退出運行的JavaScript代碼。在一個模塊中定義的變數不會自動被添加到全局共用的作用域之中,這個變數只能作用在這個作用域中。此外模塊還必須導出一些外部文件可以訪問的元素,以供其他模塊或代碼使用。

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

  • 首先是在模塊的頂部this值是undefined,這是由於在ES6中的模塊的代碼是在嚴格模式下執行的。(如果對this不是很熟悉的可以去看我的這篇文章:深入淺出this關鍵字
  • 其次,模塊不支持HTML風格的代碼註釋,這是早期瀏覽器所遺留下的JavaScript特性,在ES6的語法里不予支持。

基本用法-模塊載入

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

  • 在沒有src屬性的<script>元素中直接內嵌JavaScript代碼
  • 在<script>元素中通過src屬性指定一個地址來載入JavaScript代碼文件
  • 通過Web Worker或Service Worker的方法載入並執行JavaScript代碼

而在瀏覽器中,預設的行為就是將JavaScript作為腳本來進行載入,而非模塊。所以我們要告訴瀏覽器我們載入的是模塊,方法就是在<script>元素中,將type屬性指定為"module"。具體看下麵的示例:

1 // 第一種方式
2 <script type=""module>
3     import { add } from "./example";
4     let num = add(1, 1);
5 </script>
6 //  第二種方式
7 <script type="module" src="example.js">
8 // 第三種方式,以腳本的方式載入example.js
9 let worker = new Worker("example.js");

當HTML解析器遇到<script>元素的type="module"的時候,模塊文件就開始下載,直到文件被完全解析完成才會去執行模塊內的代碼。模塊文件是按照他們出現在HTML文件中順序執行的,也就是說無論用何種方式引入模塊,第一個<script type=""module>總是在第二個<script type=""module>之前執行。

基本用法-導出

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

export語句用於在創建JavaScript模塊時,從模塊中導出函數、對象或原始值,以便其他程式可以通過 import 語句使用它們。(
此特性目前僅在 Safari 和 Chrome 原生實現。它在許多轉換器中實現,如Traceur Compiler,Babel或Rollup。)

通過MDN的定義我們可以知道:export關鍵字可以將其放在任何函數、對象或原始值前面,從而將它們從模塊中導出。示例如下:

 1 //   ./example.js
 2 // 導出變數
 3 export var a = 1;
 4 // 導出函數
 5 export function addA(value) {
 6     return value + a;
 7 }
 8 //導出類
 9 export class add1 {
10     constructor(value) {
11         this.value = value + a;
12     }
13 }
14 //這個函數就是這個模塊所私有的,在外部不能訪問它
15 function say1() {
16     console.log('我是不是很帥');
17 }
18 //這又是個函數
19 function say2() {
20     console.log('沒錯我就是很帥');
21 }
22 //在後面對函數進行導出,它就不是私有的了
23 export say2;

需要註意的是:使用export導出的函數和類都需要一個名稱,除非使用default關鍵字,否則就不能用這個方法導出匿名函數或類。所以當我們需要導出匿名的函數或者類時,我們可以這麼做:

 1 //   ./example.js
 2 //導出匿名函數
 3 export default function(a, b) {
 4     return a + b;
 5 }
 6 //或者導出匿名的類
 7 export default class {
 8 consturctor(value) {
 9     this.value = value + 1;
10     }
11 }

具體關於default關鍵字的用法我會在後面做具體介紹,現在只需記住:當我們需要導出匿名的函數或者類時要使用export default語法。

基本語法-導入

在ES6中,從模塊中導入的功能可以通過import關鍵字。import語句由兩部分組成:要導入元素的標識符和元素應當從哪個模塊導入。

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

import 後面的大括弧中的say2表示從規定模塊導入的元素的名稱。關鍵字from後面的字元串則表示要導入的模塊的路徑,這通常是包含模塊的.js文件的相對或絕對路徑名,需要註意的是只允許使用單引號和雙引號的字元串來包裹路徑,瀏覽器使用的路徑格式與傳給<script>元素的相同,所以必須把文件的擴展名也加上。

(註:由於Node.js遵循基於文件系統首碼以區分本地文件個包的慣例,即example是一個包,而./exampple.js是一個本地文件。為了更好的相容多個瀏覽器Node.js環境,我們一定要在路徑前包含./或../來表示要導入的文件。)

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

 1 // 導入多個元素
 2 improt { a, addA, say2 } from "./example.js";
 3 console.log(a); // 1
 4 sonsole.log(addA(1); // 2
 5 
 6 // 導入整個模塊
 7 import * as example from "./example.js"
 8 console.log(example.a); // 1
 9 sonsole.log(example.addA(1); // 2
10 console.log(example.say2()); // '沒錯我就是很帥'

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

還有一點要註意的是,不管import語句把一個模塊寫了多少次,該模塊只執行一次。意思就是,在首次執行導入模塊後,實例化的模塊就會被保存在記憶體中,只要使用import語句引用它就可以重覆使用它:

1 // 首次導入需要載入模塊example.js
2 import { a } from "./example.js"
3 // 下麵的兩個import將無需載入example.js了
4 import { addA } from "./example.js"
5 import { say2 } from "./example.js"

當從模塊中導入一個元素時,它與const是一樣無法定義另一個同名變數和導入一個同名元素,也無法在import語句前使用元素或者改變導出的元素的值:

1 //接上面的代碼
2 
3 say2 = 1 ;  //會拋出一個錯誤

這是由於ES6的import語句為導入的元素創建的是只讀綁定的標識符,而不是原始綁定。因此元素只有在被導出的模塊中才可以被修改,即使是將該模塊的全部導入也無法修改其中的元素。

 1 //   ./example.js
 2 // 這是一個函數
 3 export function setA(newA) {
 4     a = newA;
 5 }
 6 //  ./say.js
 7 import { a, setA } from "./example";
 8 console.log(a);  // 1
 9 a = 2;   //拋出錯誤
10 
11 // 所以我們得這麼做
12 setA(2); 
13 console.log(a);  // 2

調用setA(2)時會返回到example.js中去執行,將a設置為2。由於say.js導入的只是a的只讀綁定的標識符而已,因此會自動進行更改。

其他基本語法

1.語法限制

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

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

由於模塊語法存在的其中一個原因是讓JavaScript引擎可以靜態地確定哪些代碼是可以導出的,因此export和import語句被設計成靜態的,不能進行任何形式的動態導出或導入。

2.重命名解決

有時在開發中,我們在導入一些元素後不想使用它們的原始名稱了,我們就可以在導出過程或者導入過程中去改變導出元素的名稱:

 1 // 導出過程
 2 function add(a, b) {
 3     return a + b;
 4 }
 5 export { add as add1 };  //在導入過程中必須使用add1作為名稱 
 6 
 7 // 導入過程
 8 import {add as add1 } from "./example"
 9 console.log(add1(1,1));  // 2
10 console.log(typeof add); //undefined

3.模塊的預設值

在CommonJS等其他的模塊化規範中,從模塊中導出或導入預設值是一個常見的用法,因此在ES6中也延用了這種用法併進行了優化。在ES6中我們可以使用default關鍵字來指定預設值,並且一個模塊只能預設一個導出值:

 1 // ./example.js
 2 // 第一種預設導出語法
 3 export default function(a, b) {
 4     return a + b;
 5 }
 6 // 第二種預設導出語法
 7 function add(a, b) {
 8     return a + b;
 9 }
10 export default add;
11 // 第三種預設導出語法
12 function add(a, b) {
13     return a + b;
14 }
15 export { add as default };

需要註意的是第三種語法,default關鍵字雖然不能作為元素的名稱,但可以作為元素的屬性名稱,因此可以使用as語法將add函數的屬性設置為default。

導入預設值的語法則是這樣的:

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

看到這裡有些朋友可能會發現,我們的第一種語法中import關鍵字後面並沒有加大括弧,認為這是錯誤的。其實這是導入預設值的獨特語法,在這的本地名稱add用於表示模塊導出的任何預設函數,這種語法是最純凈的,ES6標準創建團隊的大佬們也希望這種語法能成為web主流的模塊導入形式。

我們前面說的導入匿名函數也同樣使用這種語法:

1 //   ./example.js
2 //導出匿名函數
3 export default function(a, b) {
4     return a + b;
5 }
6 // ./say.js
7 import add from "./example";
8 console.log(add(1,1));  // 2

在這裡本地名稱add就是用於表示上面的匿名函數的。

4.導出已導入的元素

我們同樣可以在本模塊內導出我們在本模塊內導入的元素,有以下幾種語法:

 1 //  第一種語法
 2 import { add } from ./example.js;
 3 export { add };
 4 
 5 //  第二種語法
 6 export { add } from ./example.js;
 7 
 8 //換一個名稱導出
 9 export { add as add1 } from ./example.js; //以add這個名稱導入,再以add1的名稱導出
10 
11 // 導出整個模塊
12 export *  from ./example.js;

// 最後預祝自己8月份面試成功offer滿天飛-_-||


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Win7 Eclipse 搭建spark java1.8編譯環境,JavaRDD的helloworld例子 ...
  • 現有需求:將oracle資料庫中的數據準實時同步至某ftp伺服器中,以便前端應用能定時從ftp伺服器目錄中取增量數據方法:將加工腳本寫為存儲過程,然後利用shell腳本執行該存儲過程並將增量數據導出為txt文件並傳送到ftp伺服器,利用crontab定時每5分鐘執行一次shell腳本,從而實現ora... ...
  • 前序 縱觀每個優質項目,無論web端還是native原生應用開發,彈窗都是不可忽視的一環,能很大程度上直接決定用戶體驗。如:微信、支付寶、ios都有很成熟的一套彈窗UI展示場景。 最近一直沉迷在react-native開發研究中,學習起來發現沒有想象的難,不過也採坑了不少。鑒於之前有基於h5和小程式 ...
  • 1.指向單個變數的指針; int a,*p; p=&a; 2.數組的指針 (1)一維數組的指針 int a[10],*p; p=a; (2)二維數組的指針 (1)列指針 int a[3][4],*p; p=&a[0][0]; or p=a[0]; or p=*a; (2)行指針 (指向數組的指針) ...
  • jQuery介紹 jQuery是一個輕量級JS庫,使用十分簡單; jQuery的核心是選擇器,用於獲取頁面元素; jQuery提供了大量高效的方法,開發速度大幅提升; jQuery選擇器 jQuery選擇器用於選中需要操作的頁面元素 語法1:jQuery(選擇器表達式) 語法2:$(選擇器表達式) ...
  • 今天在項目中,看到了flat的一個語法,是我之前沒有用過的,所以有必要記錄下來,作為新的知識點,鞏固我自己的知識點; 附贈轉載連接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Arr ...
  • 目錄 為什麼要討論this this是什麼 如何改變this的指向 為什麼要討論this this是什麼 如何改變this的指向 為什麼要討論this 代碼一: 會列印出什麼呢?是 ' I am aa ' 嗎, 結果是: 原因: 此時 this 指向 window 對象, this.aa 等同於 w ...
  • 居中是我們使用css來佈局時常遇到的情況。使用css來進行居中時,有時一個屬性就能搞定,有時則需要一定的技巧才能相容到所有瀏覽器,本文就居中的一些常用方法做個簡單的介紹。 註:本文所講方法除了特別說明外,都是相容IE6+、谷歌、火狐等主流瀏覽器的。 先來說幾種簡單的、人畜無害的居中方法: 1. 把m ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...