到目前为止,已经实习了3个月的时间了。最近在面试,在面试题里面有题目涉及到模块循环加载的知识。趁着这个机会,将commonjs模块与es6模块之间一些重要的的区别做个总结。语法上有什么区别就不具体说了,主要谈谈引用的区别。

commonjs

对于基本数据类型,属于复制。即会被模块缓存。同时,在另一个模块可以对该模块输出的变量重新赋值。

对于复杂数据类型,属于浅拷贝。由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块。

当使用require命令加载某个模块时,就会运行整个模块的代码。

当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,commonjs模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。

循环加载时,属于加载时执行。即脚本代码在require的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。

ES6模块

es6模块中的值属于【动态只读引用】。

对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。

对于动态来说,原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。

循环加载时,

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

commonjs

对于基本数据类型,属于复制。即会被模块缓存。同时,在另一个模块可以对该模块输出的变量重新赋值。

// 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

对于复杂数据类型,属于浅拷贝。由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块。

// 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

仔细说明一下整个过程。

在Node.js中执行c模块。此时遇到require关键字,执行a.js中所有代码。

在a模块中exports之后,通过require引入了b模块,执行b模块的代码。

在b模块中exports之后,又require引入了a模块,此时执行a模块的代码。

a模块只执行exports.done = false这条语句。

回到b模块,打印b.js-1, exports, b.js-2。b模块执行完毕。

回到a模块,接着打印a.js-1, exports, b.js-2。a模块执行完毕

回到c模块,接着执行require,需要引入b模块。由于在a模块中已经引入过了,所以直接就可以输出值了。

结束。

从以上结果和分析过程可以看出,当遇到require命令时,会执行对应的模块代码。当循环引用时,有可能只输出某模块代码的一部分。当引用同一个模块时,不会再次加载,而是获取缓存。

ES6模块

es6模块中的值属于【动态只读引用】。只说明一下复杂数据类型。

对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。

对于动态来说,原始值发生变化,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

循环加载时,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();

node a.js

foo

bar

执行完毕

// 执行结果也有可能是

foo

bar

foo

bar

执行完毕

执行完毕

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

以上这篇详谈commonjs模块与es6模块的区别就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

es6 混合commjs_详谈commonjs模块与es6模块的区别相关推荐

  1. es6 混合commjs_前端模块化——CommonJS、ES6规范

    什么叫模块化? 对于一个复杂的程序,将其按照一定的规范封装成几个文件块,每一块向外暴露一些接口,但是块的内部数据是私有的,块与块之间通过接口通信.这个过程称为模块化. 模块化的好处 CommonJS ...

  2. 关于JavaScript的模块(CommonJS, AMD, CMD, ES6模块)的理解

    Javascript模块化就是解决将代码进行分隔,作用域隔离,模块之间的依赖管理等多个方面问题. 这样的优点不言而喻:1.可维护性2.命名空间私有化,可以避免污染全局环境3.代码重用,通过模块可以方便 ...

  3. 详解CommonJS模块与ES6模块

    详解CommonJS模块与ES6模块 历史上,JS一直没有模块体系,在ES6之前,最主要的是CommonJS和AMD两种.前者用于服务器,后者用于浏览器,ES6在语言标准的层面上实现了模块功能,使用简 ...

  4. ES6模块加载方案 CommonJS和AMD ES6和CommonJS

    目录 CommonJS CommonJS和AMD的对比 ES6和CommonJS 改成ES6 exports和module.exports CommonJS 每个文件就是一个模块,有自己的作用域.在一 ...

  5. CommonJS,AMD,CMD,ES6,require 和 import 详解

    CommonJS,AMD,CMD,ES6 commonJS用同步的方式加载模块.在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题.但是在浏览器端,限于网络原因,更合理的方案是使用异步 ...

  6. Nodejs模块、自定义模块、CommonJs的概念和使用

    场景 CommonJs JavaScript 是一个强大面向对象语言,它有很多快速高效的解释器.然而, JavaScript 标准定义的API 是为了构建基于浏览器的应用程序.并没有制定一个用于更广泛 ...

  7. Commonjs规范及Node模块实现

    前面的话 Node在实现中并非完全按照CommonJS规范实现,而是对模块规范进行了一定的取舍,同时也增加了少许自身需要的特性.本文将详细介绍NodeJS的模块实现 引入 nodejs是区别于java ...

  8. es6箭头函数_【知识点】ES6箭头函数、箭头函数与普通函数的区别

    作者:長安曹公子 文章出处:ES6 - 箭头函数.箭头函数与普通函数的区别 一.基本语法 ES6中允许使用箭头=>来定义箭头函数,具体语法,我们来看一个简单的例子: // 箭头函数 let fu ...

  9. python oserror路径not found_详谈Python3 操作系统与路径 模块(os / os.path / pathlib)

    python如何判断一个目录下是否存在某个文件?如果小编突然在人群中很疯或者沉默,那时一定很难过. 1.使用os模块 用os模块中os.path.exists()方法检测是否存在test_file.t ...

最新文章

  1. 瞧瞧,这样的代码才叫Pythonic
  2. Apache用户目录枚举工具apache-users
  3. 006_Radio单选框
  4. 深入理解Java虚拟机知乎_深入理解Java虚拟机(类文件结构)
  5. 作业21-加载静态文件,父模板的继承和扩展
  6. C语言十六进制转换为八进制(附完整源码)
  7. 【IfICan】脚步很乱!
  8. Hibernate Search 4.2最终发布:支持空间查询
  9. android webview capturepicture,android webView截图的4种方法
  10. java中拦截器 过滤器 监听器都有什么区别
  11. Windows Live Writer
  12. 一张报表节约几十万能耗,新华扬解密精益生产的精髓
  13. 联想交换机服务器型号,联想EN1032交换机 ISL vLAG配置
  14. matlab 模的平方,RSA模重复平方算法小示例
  15. 小学计算机的一些课题,小学计算机课题研究价值主要体现在什么方面
  16. 【读书笔记】文案创作完全手册
  17. arduino 读取模拟电压_基础部分-读取模拟电压
  18. STM32 烧录程序后上电不工作,但调试模式下可正常工作的解决办法
  19. ElasticSearch 在 Spring 项目中的实践
  20. 长沙理工计算机竞赛黑马,2018年五大学科成绩出炉,长沙杀出一匹黑马!

热门文章

  1. spyder 崩溃解决方案
  2. java 自定义arraylist_Java 中模仿源码自定义ArrayList
  3. Leetcode 322. 零钱兑换 (每日一题 20210824)
  4. Tensorflow实现MNIST数据自编码(3)
  5. ubuntu~快捷键
  6. R语言实战案例-蒙特卡罗方法(附实现代码)
  7. 编程语言中的Lambda 函数是如何产生的 它究竟有什么用
  8. R语言实战应用精讲50篇(十三)-如何使用JAVA调用R语言,两种语言的完美结合
  9. LeetCode-链表-203. 移除链表元素
  10. 读取word文件中的文本信息