(自用面试题)CommonJS模块、ES6模块的区别是什么?

88 阅读2分钟
  1. CommonJS模块输出的是一个值的拷贝(浅拷⻉),一旦输出一个值,模块内部的变量就影响不到这个值,除非写成一个函数。ES6模块输出的是值的引用,但变量指向的地址是只读的,不能重新赋值,类似于const。

eg:

// lib.js 
var counter = 3function incCounter() {   
  counter++; 
} 
module.exports = {   
  counter: counter,   
  incCounter: incCounter, 
};

// main.js 
var mod = require('./lib'); 
console.log(mod.counter);  // 3 
mod.incCounter(); 
console.log(mod.counter); // 3


// lib.js模块加载以后,它的内部变化就影响不到输出的mod.counter了。这是因为mod.counter是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。


// lib.js 
var counter = 3function incCounter() {   
  counter++; 
} 
module.exports = {   
  get counter() {     
    return counter   
  },   
  incCounter: incCounter, 
};

// main.js 
var mod = require('./lib'); 
console.log(mod.counter);  // 3 
mod.incCounter(); 
console.log(mod.counter); // 4

eg:

// lib.js 
export let counter = 3export function incCounter() {   
  counter++; 
} 

// main.js 
import { counter, incCounter } from './lib'console.log(counter); // 3 
incCounter(); 
console.log(counter); // 4


// main.js从lib.js输入变量obj,可以对obj添加属性,但是重新赋值就会报错。因为变量obj指向的地址是只读的,不能重新赋值,这就好比main.js创造了一个名为obj的const变量。
// lib.js 
export let obj = {}; 
// main.js 
import { obj } from './lib'; 
obj.prop = 123// OK 
obj = {}; // TypeError
  1. CommonJS模块是运行时加载,它加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。ES6模块是编译时输出接口,这种加载被称为编译时加载或者静态加载,即ES6模块可以在编译时就完成模块加载,效率要比CommonJS模块的加载方式高;当然这也导致了没法引用ES6模块本身,因为它不是对象

eg:

// CommonJS模块
let { stat, exists, readFile } = require('fs')

// 等同于let _fs = require('fs')
let stat = _fs.stat
let exists = _fs.exists
let readfile = _fs.readfile;

上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取3个方法。这种加载称为运行时加载,因为只有运行时才能得到这个对象,导致完全没办法在编译时做静态优化

eg:

// ES6模块
import { stat, exists, readFile } from 'fs';

上面代码的实质是从fs模块加载3个方法,其他方法不加载。这种加载称为编译时加载或者静态加载,即ES6模块可以在编译时就完成模块加载,效率要比CommonJS模块的加载方式高。当然这也导致了没法引用ES6模块本身,因为它不是对象。