CommonJS模块与ES6模块的区别

本文转自 https://www.cnblogs.com/unclekeith/archive/2017/10/17/7679503.html

CommonJS

  1. 对于基本数据类型,属于复制。即会被模块缓存。同时,在另一个模块可以对该模块输出的变量重新赋值。
  2. 对于复杂数据类型,属于浅拷贝。由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块。
  3. 当使用require命令加载某个模块时,就会运行整个模块的代码。
  4. 当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。
  5. 循环加载时,属于加载时执行。即脚本代码在require的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。

ES6模块

  1. ES6模块中的值属于【动态只读引用】。
  2. 对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
  3. 对于动态来说,原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
  4. 循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就能够执行。

上面说了一些重要区别。现在举一些例子来说明每一点吧

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(); babel-node a.js
foo
bar
执行完毕 // 执行结果也有可能是
foo
bar
foo
bar
执行完毕
执行完毕

由于在两个模块之间都存在引用。因此能够正常执行。

以上以上。对ES6 module和CommonJSmodule有不了解的同学可以参考一下以下的文章哈

关于commJS 和 es6 的一些区别的更多相关文章

  1. commonjs模块和es6模块的区别

    commonjs模块与es6模块的区别 到目前为止,已经实习了3个月的时间了.最近在面试,在面试题里面有题目涉及到模块循环加载的知识.趁着这个机会,将commonjs模块与es6模块之间一些重要的的区 ...

  2. commonjs模块和es6模块的区别?

    commonjs模块和es6模块最主要的区别:commonjs模块是拷贝,es6模块是引用,但理解这些,先得理解对象复制的问题,在回过头来理解这两模块的区别. 一.基本数据类型的模块 ./a1.js ...

  3. commonJS模块规范 和 es6模块规范 区别

    ES6 模块与 CommonJS 模块的差异 CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口. Commo ...

  4. 浅谈ES5和ES6继承和区别

    最近想在重新学下ES6,所以就把自己学到的,记录下加强下自己的理解 首先先简单的聊下ES5和ES6中的继承 1.在es5中的继承: function parent(a,b){ this a = a; ...

  5. js求和运算在可变参数的情况下ES3、ES5和ES6的写法区别

    //ES3.ES5的写法 function foo(){ var arr = Array.prototype.slice.call(arguments); var sum = 0; arr.forEa ...

  6. TypeScript 与 es6 写法的区别

    import 方式 ts 默认对于 commonjs 的模块是这样加载的:import * as React from 'react'; es6:import React from 'react'; ...

  7. jquery Promise和ES6 Promise的区别

    1. Deferred对象有resolve和reject方法,可以直接修改状态 jquery用Deferred实现了Promise规范,Deferred与ES6 Promise的最大区别是: Defe ...

  8. ES6 WeakMap Map 区别

    WeakMap与Map的区别 1.WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名. 2.WeakMap的键名所指向的对象,不计入垃圾回收机制. 示例: const wm ...

  9. 再次梳理AMD、CMD、CommonJS、ES6 Module的区别

    AMD AMD一开始是CommonJS规范中的一个草案,全称是Asynchronous Module Definition,即异步模块加载机制.后来由该草案的作者以RequireJS实现了AMD规范, ...

随机推荐

  1. ansible搭建mysql主主模式

    ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet.cfengine.chef.func.fabric)等优点,实现了批量系统配置.批量程序部署.批量运行命 ...

  2. CSS 子元素选择器

    所有选择器参考手册:http://www.w3school.com.cn/cssref/css_selectors.asp 1)子元素选择器 参考链接:http://www.w3school.com. ...

  3. 【转载】Failed to load class "org.slf4j.impl.StaticLoggerBinder".问题解决

    在进行hibernate配置好后运行测试类的时候出现: SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder" ...

  4. 新手做2D手游该用哪些工具?

    全球手游行业规模将突破250亿美元,越来越多的开发者开始进入手游研发领域,而作为一名菜鸟,很多时候,如果没有其他开发者的建议,会走很多弯路.一开始进入游戏研发领域的时候,你很难知道该选择什么工具.什么 ...

  5. [MST] Loading Data from the Server using lifecycle hook

    Let's stop hardcoding our initial state and fetch it from the server instead. In this lesson you wil ...

  6. MYSQL源代码编译的变动

    Mysql的安装,对于mysql不同版本号的mysql源代码编译方式不一样 5.6.2的版本号開始编译方式已经由 configure 变成了cmake方式 ,相关的新的 编译方式在mysql官网已经提 ...

  7. 32.AngularJS 表达式

    转自:https://www.cnblogs.com/best/tag/Angular/ AngularJS 表达式写在双大括号内:{{ expression }}. AngularJS 表达式把数据 ...

  8. django session深入

    转至原文  https://www.cnblogs.com/zhaof/p/6281468.html 基于cookie做用户验证时:敏感信息不适合放在cookie中 session依赖cookie s ...

  9. css五种定位方式介绍

    1.static定位(普通流定位) -------------- 默认定位 2.float定位(浮动定位) 例:float:left; 有两个取值:left(左浮动)和right(右浮动).浮动元素会 ...

  10. tomcat web容器工作原理

    Tomcat的模块结构设计的相当好,而且其Web 容器的性能相当出色.JBoss直接就使用了Tomcat的web容器,WebLogic的早期版本也是使用了Tomcat的代码.Web容器的工作过程在下面 ...