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

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

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

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

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

一、模塊化的前世今生

在 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ù)器端的文件存儲在本地硬盤,讀取速度極快,因此 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)行時加載:模塊在代碼執(zhí)行階段才被加載。
  • 同步加載:代碼按照編寫順序同步執(zhí)行,阻塞后續(xù)代碼直至模塊加載完成。
  • 值的拷貝:導(dǎo)出的是值的副本(對于基本數(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)

  • 編譯時輸出接口:在代碼解析階段(編譯時)即可確定依賴關(guān)系。
  • 異步加載:支持異步加載機(jī)制,適應(yīng)網(wǎng)絡(luò)請求環(huán)境。
  • 值的引用:導(dǎo)出的是值的動態(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ī)制、加載時機(jī)和加載方式三個維度進(jìn)行剖析。

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

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

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

代碼演示:

場景:我們定義一個 age 變量和一個自增函數(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)出時刻的拷貝)

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

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

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

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

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

  • CommonJS (同步)
    主要用于服務(wù)器端。文件都在本地磁盤,讀取時間通常在毫秒級,同步加載不會造成明顯的性能瓶頸。
  • ES Modules (異步)
    設(shè)計(jì)之初就考慮了瀏覽器環(huán)境。在瀏覽器中,模塊需要通過網(wǎng)絡(luò)請求加載,網(wǎng)絡(luò)延遲不可控。如果采用同步加載,會阻塞主線程,導(dǎo)致頁面“假死”無法交互。因此,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ā)中最容易踩坑的地方。

場景 A:CommonJS 引用 ES Modules

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

  • 常規(guī)方案:使用異步的動態(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)部沒有頂級 await)。

    node --experimental-require-module index.cjs
    

場景 B:ES Modules 引用 CommonJS

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

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

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

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

    正確寫法

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

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

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

高分回答策略

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

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

3. 工程化價值(架構(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 這種動態(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

东阳市| 大宁县| 旌德县| 连云港市| 科技| 汝城县| 佛山市| 登封市| 鄂伦春自治旗| 溧水县| 邯郸县| 即墨市| 格尔木市| 秀山| 景谷| 勐海县| 望江县| 洮南市| 曲靖市| 民勤县| 景德镇市| 石泉县| 嘉定区| 南雄市| 莱西市| 汤阴县| 光山县| 民县| 丰县| 普兰店市| 扶绥县| 南昌市| 奈曼旗| 丰台区| 当雄县| 灌阳县| 静海县| 五华县| 深水埗区| 岳普湖县| 彰武县|