关于commJS 和 es6 的一些区别

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有不了解的同学可以参考一下以下的文章哈

原文地址:https://www.cnblogs.com/czy960731/p/10291783.html

时间: 2024-08-04 09:44:20

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

commonjs模块和es6模块的区别?

commonjs模块和es6模块最主要的区别:commonjs模块是拷贝,es6模块是引用,但理解这些,先得理解对象复制的问题,在回过头来理解这两模块的区别. 一.基本数据类型的模块 ./a1.js es6模块 export var a1 = 1; setTimeout(() => a1 = 2, 500); ./a2.js commojs模块 var a2 = 2; module.exports = a2; setTimeout(() => a2 = 3 ,500); ./index.js

ES5 和ES6 继承的区别

继承:一个对象直接使用另一个对象的属性和方法 在ES5的继承中,先创建子类的实例对象this,然后再将父类的方法添加到this上( Parent.apply(this) ). ES6采用的是先创建父类的实例this(故要先调用 super( )方法),完后再用子类的构造函数修改this 上图的例子: ES5 原理: ES6 原文地址:https://www.cnblogs.com/xiaoeshuang/p/12192258.html

ES6和ES5变量声明的区别(var let const)

// es5的语法与es6的语法区别 // var let const console.log(name);//undefine,不会报错,因为变量声明会提到作用域的最前面 var name="aa"; for(var i=1;i<7;i++){ console.log(i); } console.log(i);//8,不会报错因为var声明的变量没有块级作用域 /** * 总结 var 声明的变量,声明语句会提到作用域的最前面,并且没有块级作用域 */ console.log(

ES6

我相信,在ECMAScript.next到来的时候,我们现在每天都在写的JavaScript代码将会发生巨大的变化.接下来的一年将会是令JavaScript开发者们兴奋的一年,越来越多的特性提案将被最终敲定,新一版本的JavaScript将会慢慢得到普及. 本文中,我将会讲几个我个人很期待的,希望能在2013年或者更晚一点使用上的新特性. ES.next目前的实现情况 可以通过查看Juriy Zaytsev总结的ECMAScript 6兼容性表格,和Mozilla的ES6实现情况页面以及通过使用

ES6新特性:使用新方法定义javascript的Class

ES6中定义类的方式, 就是ES3和ES5中定义类的语法糖,虽然也有些区别,但是整体定义类的方式更加简洁,类的继承更加方便, 如果想对ES6中的继承更加熟悉, 最好了解ES5中原型继承的方式, 博客园中说JS继承的文章很多, 想要深入了解的同学自己去搜: 定义一个class: 每一个使用class方式定义的类默认都有一个constructor函数, 这个函数是构造函数的主函数, 该函数体内部的this指向生成的实例, say() {}为原型上的方法, 我们定义一个简单的类 : "use stri

Es6模块语法

/** * Created by Administrator on 2017/4/15. */ /*---------------------export命令---------------------*/ //Es6 的模块加载 //defer是"渲染完再执行",async是"下载完就执行". //浏览器对于带有type="module"的<script>,是异步加载,等同于打开了<script>标签的defer属性. /

探索 模块打包 exports和require 与 export和import 的用法和区别

菜单快捷导航: CommonJS 之 exports和require用法 ES6 Module 之 export 和 import 用法 CommonJS和ES6 Module的区别 循环依赖 和 解决办法 模块打包原理简析 1.CommonJS 之 exports和require用法 CommoneJS规定每个文件是一个模块.将一个JavaScript文件直接通过script标签引入页面中,和封装成CommonJS模块最大的不同在于:前者的顶层作用域是全局作用域,在进行变量及函数声明时会污染全

ES6 从6入门到10放弃

ES6 从6入门到10放弃(耐心) 语法本身并不重要,怎么解决问题!怎么更好的解决问题才是本质 ES6 --> ES7.8.9.10 利用新技能拓宽解决问题的思路 全新的JavaScript体系 字符串模板(直接赋值变量,支持表达式) 监听数据 自定义数据结构,遍历 ES7 --> ES10 全新体系,改变JS书写习惯 学习方法 体验乐趣 1天 进入状态 10天 养成习惯 1个月后 收获知识 3个月后 行程思维习惯 5个月后 ES6 基础语法 _作用域 Let & Const 全局作用

怎样删除服务

我相信,在ECMAScript.next到来的时候,我们现在每天都在写的JavaScript代码将会发生巨大的变化.接下来的一年将会是令JavaScript开发者们兴奋的一年,越来越多的特性提案将被最终敲定,新一版本的JavaScript将会慢慢得到普及. 本文中,我将会讲几个我个人很期待的,希望能在2013年或者更晚一点使用上的新特性. ES.next目前的实现情况 可以通过查看Juriy Zaytsev总结的ECMAScript 6兼容性表格,和Mozilla的ES6实现情况页面以及通过使用