commonjs模塊和es6模塊的區別

来源:http://www.cnblogs.com/unclekeith/archive/2017/10/17/7679503.html
-Advertisement-
Play Games

commonjs模塊與es6模塊的區別 到目前為止,已經實習了3個月的時間了。最近在面試,在面試題裡面有題目涉及到模塊迴圈載入的知識。趁著這個機會,將commonjs模塊與es6模塊之間一些重要的的區別做個總結。語法上有什麼區別就不具體說了,主要談談引用的區別。 轉載請註明出處: "commonjs ...


commonjs模塊與es6模塊的區別

到目前為止,已經實習了3個月的時間了。最近在面試,在面試題裡面有題目涉及到模塊迴圈載入的知識。趁著這個機會,將commonjs模塊與es6模塊之間一些重要的的區別做個總結。語法上有什麼區別就不具體說了,主要談談引用的區別。

轉載請註明出處:commonjs模塊與es6模塊的區別

commonjs

  1. 對於基本數據類型,屬於複製。即會被模塊緩存。同時,在另一個模塊可以對該模塊輸出的變數重新賦值。
  2. 對於複雜數據類型,屬於淺拷貝。由於兩個模塊引用的對象指向同一個記憶體空間,因此對該模塊的值做修改時會影響另一個模塊。
  3. 當使用require命令載入某個模塊時,就會運行整個模塊的代碼。
  4. 當使用require命令載入同一個模塊時,不會再執行該模塊,而是取到緩存之中的值。也就是說,commonjs模塊無論載入多少次,都只會在第一次載入時運行一次,以後再載入,就返回第一次運行的結果,除非手動清除系統緩存。
  5. 迴圈載入時,屬於載入時執行。即腳本代碼在require的時候,就會全部執行。一旦出現某個模塊被"迴圈載入",就只輸出已經執行的部分,還未執行的部分不會輸出。

ES6模塊

  1. es6模塊中的值屬於【動態只讀引用】。
  2. 對於只讀來說,即不允許修改引入變數的值,import的變數是只讀的,不論是基本數據類型還是複雜數據類型。當模塊遇到import命令時,就會生成一個只讀引用。等到腳本真正執行時,再根據這個只讀引用,到被載入的那個模塊裡面去取值。
  3. 對於動態來說,原始值發生變化,import載入的值也會發生變化。不論是基本數據類型還是複雜數據類型。
  4. 迴圈載入時,

上面說了一些重要區別。現在舉一些例子來說明每一點吧

commonjs

  1. 對於基本數據類型,屬於複製。即會被模塊緩存。同時,在另一個模塊可以對該模塊輸出的變數重新賦值。
// b.js
let count = 1
let plusCount = () => {
  count++
}
setTimeout(() => {
  console.log('b.js-1', count)
}, 1000)
module.exports = {
  count,
  plusCount
}

// a.js
let mod = require('./b.js')
console.log('a.js-1', mod.count)
mod.plusCount()
console.log('a.js-2', mod.count)
setTimeout(() => {
    mod.count = 3
    console.log('a.js-3', mod.count)
}, 2000)

node a.js
a.js-1 1
a.js-2 1
b.js-1 2  // 1秒後
a.js-3 3  // 2秒後

以上代碼可以看出,b模塊export的count變數,是一個複製行為。在plusCount方法調用之後,a模塊中的count不受影響。同時,可以在b模塊中更改a模塊中的值。如果希望能夠同步代碼,可以export出去一個getter。

// 其他代碼相同
module.exports = {
  get count () {
    return count
  },
  plusCount
}

node a.js
a.js-1 1
a.js-2 1
b.js-1 2  // 1秒後
a.js-3 2  // 2秒後, 由於沒有定義setter,因此無法對值進行設置。所以還是返回2
  1. 對於複雜數據類型,屬於淺拷貝。由於兩個模塊引用的對象指向同一個記憶體空間,因此對該模塊的值做修改時會影響另一個模塊。
// b.js
let obj = {
  count: 1
}
let plusCount = () => {
  obj.count++
}
setTimeout(() => {
  console.log('b.js-1', obj.count)
}, 1000)
setTimeout(() => {
  console.log('b.js-2', obj.count)
}, 3000)
module.exports = {
  obj,
  plusCount
}

// a.js
var mod = require('./b.js')
console.log('a.js-1', mod.obj.count)
mod.plusCount()
console.log('a.js-2', mod.obj.count)
setTimeout(() => {
  mod.obj.count = 3
  console.log('a.js-3', mod.obj.count)
}, 2000)

node a.js
a.js-1 1
a.js-2 2
b.js-1 2
a.js-3 3
b.js-2 3

以上代碼可以看出,對於對象來說屬於淺拷貝。當執行a模塊時,首先列印obj.count的值為1,然後通過plusCount方法,再次列印時為2。接著在a模塊修改count的值為3,此時在b模塊的值也為3。

3.當使用require命令載入某個模塊時,就會運行整個模塊的代碼。

4.當使用require命令載入同一個模塊時,不會再執行該模塊,而是取到緩存之中的值。也就是說,commonjs模塊無論載入多少次,都只會在第一次載入時運行一次,以後再載入,就返回第一次運行的結果,除非手動清除系統緩存。

5.迴圈載入時,屬於載入時執行。即腳本代碼在require的時候,就會全部執行。一旦出現某個模塊被"迴圈載入",就只輸出已經執行的部分,還未執行的部分不會輸出。

3, 4, 5可以使用同一個例子說明

// b.js
exports.done = false
let a = require('./a.js')
console.log('b.js-1', a.done)
exports.done = true
console.log('b.js-2', '執行完畢')

// a.js
exports.done = false
let b = require('./b.js')
console.log('a.js-1', b.done)
exports.done = true
console.log('a.js-2', '執行完畢')

// c.js
let a = require('./a.js')
let b = require('./b.js')

console.log('c.js-1', '執行完畢', a.done, b.done)

node c.js
b.js-1 false
b.js-2 執行完畢
a.js-1 true
a.js-2 執行完畢
c.js-1 執行完畢 true true

仔細說明一下整個過程。

  1. 在Node.js中執行c模塊。此時遇到require關鍵字,執行a.js中所有代碼。
  2. 在a模塊中exports之後,通過require引入了b模塊,執行b模塊的代碼。
  3. 在b模塊中exports之後,又require引入了a模塊,此時執行a模塊的代碼。
  4. a模塊只執行exports.done = false這條語句。
  5. 回到b模塊,列印b.js-1, exports, b.js-2。b模塊執行完畢。
  6. 回到a模塊,接著列印a.js-1, exports, b.js-2。a模塊執行完畢
  7. 回到c模塊,接著執行require,需要引入b模塊。由於在a模塊中已經引入過了,所以直接就可以輸出值了。
  8. 結束。

從以上結果和分析過程可以看出,當遇到require命令時,會執行對應的模塊代碼。當迴圈引用時,有可能只輸出某模塊代碼的一部分。當引用同一個模塊時,不會再次載入,而是獲取緩存。

ES6模塊

  1. es6模塊中的值屬於【動態只讀引用】。只說明一下複雜數據類型。
  2. 對於只讀來說,即不允許修改引入變數的值,import的變數是只讀的,不論是基本數據類型還是複雜數據類型。當模塊遇到import命令時,就會生成一個只讀引用。等到腳本真正執行時,再根據這個只讀引用,到被載入的那個模塊裡面去取值。
  3. 對於動態來說,原始值發生變化,import載入的值也會發生變化。不論是基本數據類型還是複雜數據類型。
// b.js
export let counter = {
  count: 1
}
setTimeout(() => {
  console.log('b.js-1', counter.count)
}, 1000)

// a.js
import { counter } from './b.js'
counter = {}
console.log('a.js-1', counter)

// Syntax Error: "counter" is read-only

雖然不能將counter重新賦值一個新的對象,但是可以給對象添加屬性和方法。此時不會報錯。這種行為類型與關鍵字const的用法。

// a.js
import { counter } from './b.js'
counter.count++
console.log(counter)

// 2
  1. 迴圈載入時,ES6模塊是動態引用。只要兩個模塊之間存在某個引用,代碼就能夠執行。
// b.js
import {foo} from './a.js';
export function bar() {
  console.log('bar');
  if (Math.random() > 0.5) {
    foo();
  }
}

// a.js
import {bar} from './b.js';
export function foo() {
  console.log('foo');
  bar();
  console.log('執行完畢');
}
foo();

node a.js
foo
bar
執行完畢

// 執行結果也有可能是
foo
bar
foo
bar
執行完畢
執行完畢

由於在兩個模塊之間都存在引用。因此能夠正常執行。

以上以上。對es6 module和commonjs module有不瞭解的同學可以參考一下以下的文章哈

ES6 module

module的語法

module的載入實現


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

-Advertisement-
Play Games
更多相關文章
  • 我們知道typescript 是ES 超集。這意味著,不僅僅ES 的各種語法特性都會包括,還能保證通過typescript的編譯服務可以很方便的轉成ES向下相容的版本,這得意於typescript強大的polyfill 機制。 一般來說,根據瀏覽器對ES支持的普及度,我們會把typescript的代 ...
  • script標簽用於載入腳本與執行腳本,在前端開發中可以說是非常重要的標簽了。直接使用script腳本的話,html會按照順序來載入並執行腳本,在腳本載入&執行的過程中,會阻塞後續的DOM渲染。 現在大家習慣於在頁面中引用各種的第三方腳本,如果第三方服務商出現了一些小問題,比如延遲之類的,就會使得頁 ...
  • 什麼是表單? 一個表單有三個基本組成部分: 表單標簽:這裡麵包含了處理表單數據所用CGI程式的URL以及數據提交到伺服器的方法。 表單域:包含了文本框、密碼框、隱藏域、多行文本框、覆選框、單選框、下拉選擇框和文件上傳框等。 表單按鈕:包括提交按鈕、複位按鈕和一般按鈕;用於將數據傳送到伺服器上的CGI ...
  • HTTP出錯大全 101 - Switching Protocols Top Success Codes 200 - OK201 - Created202 - Accepted203 - Non-Authoritative Information (for DNS)204 - No Content2 ...
  • 一、簡介 編寫一個會動的日曆,日曆上面有年月日,周幾,時分秒,效果如下: 年月日,周幾,時分秒都會隨著系統時間的走動而改變 二、代碼 ...
  • 方法一(純css實現): html代碼: css樣式: 缺點:只能實現一行式的打字效果,無法打出一段落文字 方法二(jquery): html代碼: jquery代碼: 方法三(jquery-typed插件): html代碼: 引入Typed.js,Typed.js內容如下 調用Typed.js 方 ...
  • PC: IE、QQ、chrome、firefox、360、safair 移動端:微信內置瀏覽器、QQ、獵豹、百度、UC、2345、系統自帶 PC: ie: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; rv ...
  • <a href="存在問題.docx" download style="color:red;font-size:20px;cursor:pointer;float:right;" onclick="">下載</a> download 可以使路徑的圖片下載 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...