commonjs与es6模块

一、es6
  • export命令
let firstName = '牛哄哄的柯南';
let lastName = 'Keafmd';
let year = 1958;
export { 
    firstName as f,   //可以使用as关键字,重命名对外接口
    lastName, 
    year 
};

上面代码是export命令的另外一种写法。 在export命令后面,使用大括号指定所要输出的一组变量。它与前一种写法(直接放置在let语句前)是等价的,但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量

  • export命令除了输出变量,还可以输出函数、类(class)
export function multiply(x, y) {
    return x * y;
};
export class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return this.x + ',' + this.y;
    }
};
  • import命令具有提升效果,会提升到整个模块的头部,首先执行:
foo();
import { foo } from './my_module.js';

上面的代码不会报错,因为import的执行早于foo的调用。这种行为的本质是,import命令是编译阶段执行的,在代码运行之前

意味着在预编译阶段,import命令会执行,相应的my_module.js会执行一次(该文件也就在这执行仅有的一次):

// /src/index.js
console.log('index.js在编译阶段执行了')
export let a = 123
setTimeout(() => {
    a = 456
}, 3000)

// /index.html
<script type="module">
    import {a} from './src/index.js'
    console.log(a)
    setTimeout(() => {
        console.log(a)
    }, 3000)
</script>

// 结果
index.js在编译阶段执行了
123
456
  • import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构
// 报错
import { 'f' + 'oo' } from './my_module.js';
// 报错
let module = './my_module.js';
import { foo } from module;

// 报错
if (x === 1) {
  import { foo } from './module1.js';
} else {
  import { foo } from './module2.js';
}
  • 如果多次重复执行同一句import语句,那么只会执行一次,而不会执行多次
import './lodash.js';
import './lodash.js';

// 上面代码加载了两次lodash,但是只会执行一次
  • export default命令用在非匿名函数前,也是可以的:
// export-default.js
export default function foo () {
    console.log('foo');
}
// 或者写成
function foo() {
    console.log('foo');
}
export default foo;

上面代码中,foo函数的函数名foo,在模块外部是无效的。加载的时候,视同匿名函数加载

参考:https://blog.csdn.net/weixin_43883917/article/details/110352384?utm_source=app&app_version=4.5.7

二、commonjs与es6模块区别

Commonjs

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

es6模块

  • ES6模块中的值属于动态只读引用
  • 对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。
  • 当模块遇到import命令时(在预编译阶段遇到的import命令,接着执行import的文件,最后生成一个只读引用),就会生成一个只读引用。等到脚本(运行阶段)真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
  • 对于动态来说,原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
  • 循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就能够执行。

参考:https://blog.csdn.net/qq_36772866/article/details/88757140

三、循环加载引用

commonjs

// 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模块的代码。
  • 因为已经require过一次a模块了,因此直接取缓存中的a模块,此时缓存中的a模块只有执行exports.done = false这条语句后的内容。
  • 回到b模块,打印b.js-1, exports, b.js-2b模块执行完毕。
  • 回到a模块,接着打印a.js-1, exports, b.js-2a模块执行完毕
  • 回到c模块,接着执行require,需要引入b模块。由于在a模块中已经引入过了,所以直接就可以输出值了。
  • 结束。
  • 从以上结果和分析过程可以看出,当遇到require命令时,会执行对应的模块代码。当循环引用时,有可能只输出某模块代码的一部分。当引用同一个模块时,不会再次加载,而是获取缓存。

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
执行完毕
执行完毕

说明一下流程:

  • a.js编译,执行import命令,执行b.js
  • b.js编译,执行import命令,执行a.js
  • a.js已经import过了,因此不执行,b.js中的foo是对a.js中的foo的动态引用
  • 继续执行b.js,导出bar
  • 回到a.js继续执行,导出foo,执行foo

补充一点:如果在b.js中提前使用a.js导出的内容会报错:

// b.js
import {foo} from './a.js';  // 只是添加引用,还得看它是否已经export foo
console.log(foo)  // 此时a.js还未export foo,因此会报错
export function bar() {
  console.log('bar');
  if (Math.random() > 0.5) {
    foo();
  }
}

可见,在循环引用中,b.js模块中使用导入a.js的变量只能是放在导出export的函数中,因为a.jsimport是在编译阶段执行的,始终是比执行阶段的代码先执行,而此时又要执行完b.js,意味着在执行完b.js整个过程中,foo都未被export,因此b.js直接使用foo是获取不到的

四、一些问题
  • commonjs中的顶层this指向所在模块
  • es6中的顶层this指向undefined
  • es6export/export default是在执行阶段执行的,而且不会提升,按照顺序执行,就是普通的执行代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值