最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript中CommonJS 與 ES Modules的區(qū)別

 更新時(shí)間:2026年03月05日 09:29:42   作者:NEXT06  
本文主要介紹了JavaScript中CommonJS 與 ES Modules的區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

在前端工程化的演進(jìn)長(zhǎng)河中,模塊化規(guī)范的變遷是理解 JavaScript 運(yùn)行機(jī)制的關(guān)鍵一環(huán)。對(duì)于資深開發(fā)者而言,CommonJS(簡(jiǎn)稱 CJS)與 ES Modules(簡(jiǎn)稱 ESM)不僅僅是語法的區(qū)別,更代表了 JavaScript 在服務(wù)端與瀏覽器端不同運(yùn)行環(huán)境下的架構(gòu)哲學(xué)。

本文將從底層原理出發(fā),剖析這兩大規(guī)范的核心差異,并結(jié)合 Node.js 的最新特性,探討工程化場(chǎng)景下的互操作性方案。

一、模塊化的前世今生

在 ES6 之前,JavaScript 語言層面并沒有內(nèi)置的模塊體系。這導(dǎo)致早期的大型項(xiàng)目開發(fā)極易陷入全局作用域污染、依賴關(guān)系混亂(Dependency Hell)的泥潭。為了解決這一痛點(diǎn),社區(qū)涌現(xiàn)出了多種解決方案。

CommonJS 應(yīng)運(yùn)而生,它主要面向服務(wù)器端(Node.js)。由于服務(wù)器端的文件存儲(chǔ)在本地硬盤,讀取速度極快,因此 CommonJS 采用了同步加載的設(shè)計(jì)。這一規(guī)范迅速確立了 Node.js 生態(tài)的統(tǒng)治地位。

然而,隨著前端應(yīng)用的日益復(fù)雜,瀏覽器端急需一種標(biāo)準(zhǔn)化的模塊體系。ES6(ECMAScript 2015)正式推出了 ES Modules。作為官方標(biāo)準(zhǔn),ESM 旨在統(tǒng)一瀏覽器和服務(wù)器的模塊規(guī)范,憑借其靜態(tài)編譯和異步加載的特性,逐漸成為現(xiàn)代前端構(gòu)建工具(如 Vite, Webpack, Rollup)的首選。

二、兩大規(guī)范的運(yùn)行機(jī)制與特點(diǎn)

1. CommonJS (CJS)

定位:服務(wù)器端模塊規(guī)范,Node.js 的默認(rèn)模塊系統(tǒng)。

核心特點(diǎn)

  • 運(yùn)行時(shí)加載:模塊在代碼執(zhí)行階段才被加載。
  • 同步加載:代碼按照編寫順序同步執(zhí)行,阻塞后續(xù)代碼直至模塊加載完成。
  • 值的拷貝:導(dǎo)出的是值的副本(對(duì)于基本數(shù)據(jù)類型)。

代碼示例

// 導(dǎo)出:module.exports
const obj = { a: 1 };
module.exports = obj;

// 引入:require
const obj = require('./test.js');

2. ES Modules (ESM)

定位:ECMAScript 官方標(biāo)準(zhǔn),旨在實(shí)現(xiàn)瀏覽器與服務(wù)端的通用。

核心特點(diǎn)

  • 編譯時(shí)輸出接口:在代碼解析階段(編譯時(shí))即可確定依賴關(guān)系。
  • 異步加載:支持異步加載機(jī)制,適應(yīng)網(wǎng)絡(luò)請(qǐng)求環(huán)境。
  • 值的引用:導(dǎo)出的是值的動(dòng)態(tài)映射(Live Binding)。

代碼示例

// 導(dǎo)出:export
export const obj = { name: 'ESM' };
export default { name: 'Default' };

// 引入:import
import { obj } from './test.js';
import defaultObj from './test.js';

三、深度解析——核心差異

如果要深入理解兩者的區(qū)別,必須從輸出機(jī)制、加載時(shí)機(jī)和加載方式三個(gè)維度進(jìn)行剖析。

1. 輸出值的機(jī)制:值的拷貝 vs 值的引用

這是 CJS 與 ESM 最本質(zhì)的區(qū)別,也是面試中最高頻的考察點(diǎn)。

  • CommonJS:值的拷貝
    CJS 模塊輸出的是一個(gè)對(duì)象,該對(duì)象在腳本運(yùn)行完后生成。一旦輸出,模塊內(nèi)部的變化就無法影響到這個(gè)值(除非導(dǎo)出的是引用類型對(duì)象且修改了其屬性,這里特指基本數(shù)據(jù)類型或引用的替換)。
  • ES Modules:值的引用
    ESM 模塊通過 export 導(dǎo)出的是一個(gè)靜態(tài)接口。import 導(dǎo)入的變量?jī)H僅是一個(gè)指向被導(dǎo)出模塊內(nèi)部變量的“指針”。如果模塊內(nèi)部修改了該變量,外部導(dǎo)入的地方也會(huì)感知到變化。

代碼演示:

場(chǎng)景:我們定義一個(gè) age 變量和一個(gè)自增函數(shù) addAge。

CommonJS 實(shí)現(xiàn):

// lib.js
let age = 18;
module.exports = {
  age,
  addAge: function () {
    age++;
  },
};

// main.js
const { age, addAge } = require('./lib.js');
console.log(age); // 18
addAge();
console.log(age); // 18 (注意:這里依然是 18,因?yàn)閷?dǎo)出的是 age 變量在導(dǎo)出時(shí)刻的拷貝)

ES Modules 實(shí)現(xiàn):

// lib.mjs
export let age = 18;
export function addAge() {
  age++;
}

// main.mjs
import { age, addAge } from './lib.mjs';
console.log(age); // 18
addAge();
console.log(age); // 19 (注意:這里變成了 19,因?yàn)?import 獲取的是實(shí)時(shí)的綁定)

技術(shù)延伸
由于 ESM 是實(shí)時(shí)引用,它能更好地處理循環(huán)依賴問題。在 ESM 中,只要引用存在,代碼就能執(zhí)行(盡管可能在暫時(shí)性死區(qū) TDZ 中);而在 CJS 中,循環(huán)依賴可能導(dǎo)致導(dǎo)出一個(gè)不完整的對(duì)象(空對(duì)象),因?yàn)槟K可能尚未執(zhí)行完畢。此外,ESM 導(dǎo)入的變量是只讀的(Read-only),嘗試在 main.mjs 中直接執(zhí)行 age = 20 會(huì)拋出 TypeError。

2. 加載時(shí)機(jī):運(yùn)行時(shí) vs 編譯時(shí)

  • CommonJS (運(yùn)行時(shí))
    require 本質(zhì)上是一個(gè)函數(shù)。你可以將它放在 if 語句中,或者根據(jù)變量動(dòng)態(tài)生成路徑。只有當(dāng)代碼執(zhí)行到這一行時(shí),Node.js 才會(huì)去加載模塊。

    if (condition) {
      const lib = require('./lib.js'); // 條件加載
    }
    
  • ES Modules (編譯時(shí))
    import 語句(靜態(tài)導(dǎo)入)必須位于模塊頂層,不能嵌套在代碼塊中。JavaScript 引擎在編譯階段(解析 AST 時(shí))就能確定模塊的依賴關(guān)系。
    工程化價(jià)值:這使得 Tree Shaking(搖樹優(yōu)化)  成為可能。構(gòu)建工具可以在打包時(shí)靜態(tài)分析出哪些 export 沒有被使用,從而安全地刪除這些死代碼,減小包體積。

3. 加載方式:同步 vs 異步

  • CommonJS (同步)
    主要用于服務(wù)器端。文件都在本地磁盤,讀取時(shí)間通常在毫秒級(jí),同步加載不會(huì)造成明顯的性能瓶頸。
  • ES Modules (異步)
    設(shè)計(jì)之初就考慮了瀏覽器環(huán)境。在瀏覽器中,模塊需要通過網(wǎng)絡(luò)請(qǐng)求加載,網(wǎng)絡(luò)延遲不可控。如果采用同步加載,會(huì)阻塞主線程,導(dǎo)致頁(yè)面“假死”無法交互。因此,ESM 規(guī)范規(guī)定模塊解析階段是異步的。

四、工程化實(shí)踐與互操作性

在 Node.js 環(huán)境逐步過渡到 ESM 的過程中,兩者共存的情況十分常見。

1. 文件后綴與配置

在 Node.js 中,為了區(qū)分模塊類型:

  • CommonJS:通常使用 .cjs 后綴,或者在 package.json 中未設(shè)置 type 字段(默認(rèn)為 CJS)。
  • ES Modules:強(qiáng)制使用 .mjs 后綴,或者在 package.json 中設(shè)置 "type": "module"。

2. 相互引用(Interoperability)

這是開發(fā)中最容易踩坑的地方。

場(chǎng)景 A:CommonJS 引用 ES Modules

由于 CJS 是同步的 require,而 ESM 是異步加載的,因此原生 CJS 無法直接 require ESM 文件

  • 常規(guī)方案:使用異步的動(dòng)態(tài)導(dǎo)入 import() 配合 IIFE。

    // index.cjs
    (async () => {
      const { default: foo } = await import('./foo.mjs');
    })();
    
  • 新特性(Node.js v22+ / Experimental)
    Node.js 在 2024 年推出了 --experimental-require-module 標(biāo)志。開啟后,支持同步 require 加載 ESM(前提是該 ESM 模塊內(nèi)部沒有頂級(jí) await)。

    node --experimental-require-module index.cjs
    

場(chǎng)景 B:ES Modules 引用 CommonJS

ESM 的兼容性較好,可以導(dǎo)入 CJS 模塊。

  • 機(jī)制:Node.js 會(huì)將 CJS 的 module.exports 整體作為一個(gè)默認(rèn)導(dǎo)出(Default Export)處理。

  • 注意事項(xiàng)不支持具名導(dǎo)入(Named Imports)的直接解構(gòu)。雖然部分構(gòu)建工具(如 Webpack)支持混用,但在原生 Node.js 環(huán)境下,以下寫法通常會(huì)報(bào)錯(cuò)或表現(xiàn)不符合預(yù)期:

    // 錯(cuò)誤示范 (原生 Node.js)
    import { someMethod } from './lib.cjs'; // 可能會(huì)失敗,因?yàn)?CJS 只有 default 導(dǎo)出
    

    正確寫法

    import lib from './lib.cjs';
    const { someMethod } = lib;
    

五、面試場(chǎng)景復(fù)盤

面試官提問:“請(qǐng)聊聊 CommonJS 和 ESM 的區(qū)別。”

高分回答策略

1. 一句話定性(宏觀視角)
“CommonJS 是 Node.js 社區(qū)提出的服務(wù)器端運(yùn)行時(shí)模塊規(guī)范,主要特點(diǎn)是同步加載值的拷貝;而 ES Modules 是 ECMAScript 的官方標(biāo)準(zhǔn),實(shí)現(xiàn)了瀏覽器和服務(wù)端的統(tǒng)一,主要特點(diǎn)是編譯時(shí)靜態(tài)分析異步加載值的引用。”

2. 核心差異展開(技術(shù)深度)
“兩者最本質(zhì)的區(qū)別在于輸出值的機(jī)制。
CommonJS 輸出的是值的拷貝。一旦模塊輸出,內(nèi)部變量的變化不會(huì)影響導(dǎo)出值,類似于基本類型的賦值。
ES Modules 輸出的是值的引用(Live Binding) 。導(dǎo)入的變量實(shí)際上是指向模塊內(nèi)部?jī)?nèi)存地址的指針,模塊內(nèi)部變化會(huì)實(shí)時(shí)反映到外部,這使得 ESM 能更好地處理循環(huán)依賴問題。”

3. 工程化價(jià)值(架構(gòu)視角)
“在工程實(shí)踐中,ESM 的靜態(tài)編譯特性非常關(guān)鍵。因?yàn)樗试S構(gòu)建工具在代碼運(yùn)行前分析依賴關(guān)系,從而實(shí)現(xiàn) Tree Shaking,去除無用代碼,優(yōu)化包體積。這是 CommonJS 這種動(dòng)態(tài)加載規(guī)范無法做到的。”

4. 兼容性補(bǔ)充(實(shí)戰(zhàn)經(jīng)驗(yàn))
“在 Node.js 環(huán)境中,兩者互操作需要注意。ESM 可以較容易地導(dǎo)入 CJS(作為默認(rèn)導(dǎo)出),但 CJS 導(dǎo)入 ESM 通常需要異步的 import()。不過,Node.js 最近引入了 --experimental-require-module 標(biāo)志,正嘗試打破這一同步加載的壁壘。”

到此這篇關(guān)于JavaScript中CommonJS 與 ES Modules的區(qū)別的文章就介紹到這了,更多相關(guān)JavaScript CommonJS ES Modules內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

云和县| 神池县| 乐昌市| 贺州市| 修水县| 远安县| 宜阳县| 锡林浩特市| 昂仁县| 上虞市| 通许县| 突泉县| 商河县| 仲巴县| 改则县| 神农架林区| 赣榆县| 宿松县| 新乡市| 本溪市| 武威市| 潼南县| 儋州市| 富裕县| 甘孜| 遂宁市| 临朐县| 和林格尔县| 定襄县| 富裕县| 会东县| 中阳县| 洪江市| 华亭县| 三河市| 炎陵县| 巧家县| 柳林县| 青州市| 通州区| 青阳县|