ES6与CommonJS中的模块处理的区别

news/2024/7/10 23:37:38 标签: es6, JavaScript

ES6和CommonJS都有自己的一套处理模块化代码的措施,即JS文件之间的相互引用。

为了方便两种方式的测试,使用nodejs的环境进行测试

CommonJS的模块处理

使用require来引入其他模块的代码,使用module.exports来引出

// exportDemo.js
count = 1;
module.exports.count = count;
module.exports.Hello = function() {
 var name;
 this.setName = function(newName) {
 name = newName;
 }
 this.sayHello = function() {
 console.log("hello Mr." + name);
 }
 this.getId = function() {
 return count++
 }
}
// requireDemo.js
var {Hello} = require("./exportDemo")
var hello = new Hello();
 
hello.setName("Blank");
hello.sayHello();

前端全栈学习交流圈:866109386,面向1-3经验年前端开发人员,帮助突破技术瓶颈,提升思维能力,群内有大量PDF可供自取,更有干货实战项目视频进群免费领取。

在终端执行 node requireDemo.js,返回结果为’hello Mr.Blank’

导出的Hello函数是原函数的一次拷贝,修改Hello函数的属性值不会对其他require的地方造成影响

var { Hello, count } = require('./exportDemo')
var hello = new Hello();
// 让count自增
console.log(hello.getId());
console.log(hello.getId());
// 发现获取的count还是原值
console.log(count)
 
// 真正的count其实是已经改了的
var newHello = new Hello();
console.log(newHello.getId())
 
var { Hello: newHello, count: newCount } = require('./exportDemo')
console.log(newCount, 'newCount');
// 再次require,取得的newHello和之前require的Hello指向同一个拷贝
console.log(newHello === Hello)

ES6的模块处理

nodejs中运行ES6风格的代码

nodejs默认是不支持ES6的模块处理方案的。

但是在8.5.0之后,ES6代码的文件格式定为mjs后,可使用 node --experimental-modules xxx.mjs 运行。

// exportDemo.mjs
export let a = 1;
// importDemo.mjs
import {a} from './exportDemo.mjs'
console.log(a)

与CommonJS模块处理的区别

CommonJS 模块输出的是一个值的拷贝(已在上一章验证),ES6 模块输出的是值的引用。

// exportDemo.mjs
export let counter = 1;
export function incCounter() {
 counter ++;
}
// importDemo.mjs
import { counter, incCounter } from './exportDemo.mjs'
 
incCounter();
console.log(counter)        // 打印结果为2,而不是初始值的1

CommonJS模块是运行时加载,ES6模块是编译时输出接口

Nodejs此类的运行环境会在一个闭包中运行CommonJS模块代码

(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});

ES6 模块不会缓存运行结果,而是动态地去被加载的模块取值,并且变量总是绑定其所在的模块。

// exportDemo.mjs
export let a = 1;
export const b = 2;
export let obj = {};
 
// importDemo.mjs
import { a, b } from './exportDemo.mjs'
console.log(a, b)
a = 1 // 报错,TypeError: Assignment to constant variable,export出来的值是一个只读引用
obj.x = 1   // 可以改变属性值

前端全栈学习交流圈:866109386,面向1-3经验年前端开发人员,帮助突破技术瓶颈,提升思维能力,群内有大量PDF可供自取,更有干货实战项目视频进群免费领取。

在ES6模块中我们更多地要去考虑语法的问题 export default

有时候我们会在代码发现 export default obj的用法,那么这个default是用来干嘛的?

default是ES6引入的与export配套使用的关键字,用来给匿名对象、匿名函数设置默认的名字用的

export出来的值必须要有一个命名,否则从语法层次便会报错

让我们看一下以下几个会报错的错误例子

export匿名对象

export { x: 1 } // 报错,SyntaxError:Unexpected token,这是一个编译阶段的错误
 
// 正确写法
export default { x: 1 }

export匿名函数

export function() {}    // 报错,SyntaxError: Unexpected token (
 
// 正确写法
export default function() {}

循环引用(recycling loading)

在复杂的模块中,可能会出现模块间的 互相引用 。

commonJS的循环引用运行机制

// a.js
exports.loaded = false;
var b = require('./b.js')
console.log("b in a is " + JSON.stringify(b))
exports.loaded = true;
console.log("a complete")
// b.js
exports.loaded = false;
var a = require('./a.js')
console.log("a in b is " + JSON.stringify(a))
exports.loaded = true;
console.log("b complete")
// main.js
var a = require('./a.js')
var b = require('./b.js')
 
console.log("a in main is" + JSON.stringify(a))
console.log("b in main is" + JSON.stringify(b))

执行指令 nodejs main.js

时序图下的执行步骤分解图如下所示:

ES6的循环引用运行机制

一个会报错的例子

// a.mjs
import { bar } from './b.mjs'
 
console.log(bar);
export let foo = 'foo from a.mjs'
// b.mjs
import { foo } from './a.mjs'
 
console.log(foo)
 
export let bar = 'bar from b.mjs'
// main.mjs
import { foo } from './a.mjs'
import { bar } from './b.mjs'

node main.mjs

时序图下的执行步骤分解图如下所示:

前端全栈学习交流圈:866109386,面向1-3经验年前端开发人员,帮助突破技术瓶颈,提升思维能力,群内有大量PDF可供自取,更有干货实战项目视频进群免费领取。

ES6的循环引用要特别注意变量是否已被声明,若未被声明的块级作用域变量被其他模块引用时,会报错。

改进方案:循环引用中尽量去export可以提前确定的值(例如函数),其实我们总是希望去** 引用模块执行完全后最终确定的变量 。**

// a.mjs
import { bar } from './b.mjs'
 
console.log(bar());
export function foo() {
 return 'foo from a.mjs'
}
// b.mjs
import { foo } from './a.mjs'
console.log(foo());
export function bar() {
 return 'bar from b.mjs'
}
// main.mjs
import { foo } from './a.mjs'
import { bar } from './b.mjs'

node main.mjs

返回结果:

foo from a.mjs
bar from b.mjs

时序图下的执行步骤分解图如下所示:


http://www.niftyadmin.cn/n/1301033.html

相关文章

指向类成员变量的指针int A:: * p

 #include "stdafx.h" #include <iostream> #include <type_traits> using namespace std; class A {public : double o; //制造一个偏移量 int a; }; class B: public A {public: int b; }; int main() { A a1 ; …

纹理压缩简介 DXT PVR ETC

文章转载自&#xff1a;https://www.jianshu.com/p/a0d7eed9f44d 在软件开发&#xff0c;特别是三维应用中&#xff0c;纹理随处可见&#xff0c;但受限于网络环境https://www.jianshu.com/p/a0d7eed9f44d和硬件能力&#xff0c;纹理也是一大瓶颈。而且在一般的三维应用中&…

Vue 在使用中的一些小技巧

在vue的使用过程中会遇到各种场景&#xff0c;当普通使用时觉得没什么&#xff0c;但是或许优化一下可以更高效更优美的进行开发。 1. 多图表resize事件去中心化 1.1 一般情况 有时候我们会遇到这样的场景&#xff0c;一个组件中有几个图表&#xff0c;在浏览器resize的时候…

C++编译与链接过程

头文件&#xff08;.h&#xff09;是不参与编译的,一般只是编译源文件(.cpp)生成.obj. 但是.cpp里面有#include将指定头文件(其实任何文件都行)插进来, 组成完整的.cpp. 如果你不喜欢这个方式,你也可以直接在.cpp里面写好了,而不放到.h里面,载用指令拷贝进来. 好了&…

如何查看网页内存大小

1、错误的查看方法 我们在做Web端开发的时候&#xff0c;会有需要查看项目的内存占用情况&#xff0c;那么我们的查看内存的操作是这样的&#xff1a; 比如&#xff0c;在Google浏览器测试项目的时候&#xff0c;是先在Memory页签下点击“Take heap snapshot”来查看当前网页…

C++内部链接与外部链接

你曾经碰到的问题&#xff1a; 1.为什么有时会出现aaa已在bbb中重定义的错误&#xff1f; 2.为什么有时会出现无法解析的外部符号&#xff1f; 3.为什么有的内联函数的定义需要写在头文件中? 4.为什么对于模板&#xff0c;声明和定义都要写在一起&#xff1f; 编译单元 什么是…

vue中$refs的用法及作用详解

一般来讲&#xff0c;获取DOM元素&#xff0c;需document.querySelector&#xff08;".input1"&#xff09;获取这个dom节点&#xff0c;然后在获取input1的值。 但是用ref绑定之后&#xff0c;我们就不需要在获取dom节点了&#xff0c;直接在上面的input上绑定inpu…

Mac安装并使用telnet命令操作

文章转载自&#xff1a;https://blog.csdn.net/guoguo527/article/details/92810745 自从mac系统升级后&#xff0c;就无法使用telnet&#xff0c;但使用ping有时又不够准确&#xff0c;因此寻找一些可以使用telnet的操作方式。 brew install telnet 在已安装brew的情况下&a…