深度理解CommonJs和es6模块

283 阅读4分钟

CommonJs和es6模块的区别

  • node的module遵循CommonJS规范,requirejs遵循AMD,seajs遵循CMD,虽各有不同,但总之还是希望保持较为统一的代码风格
  • export / import : 只有es6 支持的导出引入
  • require: node 支持的引入
  • module.exports / exports: 只有 node 支持的导出
  • CommonJs模块是运行时加载,ES6模块是编译时输出接口。
  • CommonJS 加载的是一个对象(即module.exports属性)。该对象只有在脚本运行完才会生成。而ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态编译阶段就会生成
  • 在传统编译语言的流程中,程序中的一段源代码在执行之前会经历三个步骤,统称为编译。”分词/词法分析“ -> ”解析/语法分析“ -> "代码生成
  • CommonJs模块输出的是一个值的拷贝,ES6模块输出的是值的引用

Commonjs

  • CommonJS定义的模块分为: 模块标识(module)、模块定义(exports) 、模块引用(require)
  • module代表当前的模块,module变量是一个对象,module有一个exports属性(module.exports),是对外的接口,加载某个模块,其实是加载该模块的module.exports属性。
  • module.exports和exports的的关系如下图,都指向一块{}内存区域。
语法说明:
exports = module.exports = {};

语法使用代码

var x = 5;
var addX = function (value) {
  return value + x;
}
module.exports.x = x;
module.exports.addX = addX;

var example = require("./example.js");
console.log(example.x);
console.log(example.addX);

// module.exports.count = count; // module.exports = { count: 0}
// module.exports = count; // module.exports = 0
// exports.count = count; // // module.exports = { count: 0}
// exports.a = 30; { a: 30}
exports = count; // {}

案例代码:

//utils.js
let a = 100;
console.log(module.exports); //能打印出结果为:{}
console.log(exports); //能打印出结果为:{}
exports.a = 200//这里辛苦劳作帮 module.exports 的内容给改成 {a : 200}
exports = '指向其他内存区'//这里把exports的指向指走

//test.js
var a = require('./utils');
console.log(a) // 打印为 {a : 200}

如上代码可知: 其实require导出的内容是module.exports的指向的内存块内容,并不是exports的。 简而言之,区分他们之间的区别就是 exports 只是 module.exports的引用,辅助后者添加内容用的。

exports只辅助module.exports操作内存中的数据,辛辛苦苦各种操作数据完,累得要死,结果到最后真正被require出去的内容还是module.exports的

为了避免糊涂,尽量都用 module.exports 导出,然后用require导入

CommonJs特点:

  • 所有代码运行在模块作用域,不会污染全局作用域,比如++count,并不影响外部引用的count值
  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。
  • 模块的加载顺序,按照其在代码中出现的顺序。

ES中的模块导出导入

export 和 export default

  • export与export default均可用于导出常量、函数、文件、模块等
  • 在一个文件或模块中,export、import可以有多个,export default仅有一个
  • 通过export方式导出,在导入时要加{ },export default则不需要
  • export能直接导出变量表达式,export default不行。
  • export default就是输出一个叫做default的变量或方法
  • export default a的含义是将变量a的值赋给变量default

使用代码:

'use strict'
//导出变量
export const a = '100';
//导出方法
export const dogSay = function(){
    console.log('wang wang');
}
//导出方法第二种
function catSay(){
   console.log('miao miao');
}
function catSay1(){
   console.log('miao miao1');
}
export { catSay, catSay1 };


// export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系,以下方式不符合
export 1
var m = 1;
export m;



//export default导出
const m = 100;
export default m;

//export default const m = 100;// 这里不能写这种格式。

import

  • import命令输入的变量都是只读的
  • import是静态执行,所以不能使用表达式和变量
  • import命令具有提升效果
  • import语句是 Singleton 模式
1. import命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。
import {a} from'./xxx.js'
a = {}; // Syntax Error : 'a' is read-only;

2. 如果a是一个对象,改写a的属性是允许的。
import {a} from'./xxx.js'
a.foo = 'hello'// 合法操作

3. import命令具有提升效果,会提升到整个模块的头部,首先执行。import命令是编译阶段执行的,在代码运行之前。
foo();
import { foo } from'my_module';

4. 如果多次重复执行同一句import语句,那么只会执行一次,而不会执行多次。
import { foo } from'my_module';
import { bar } from'my_module';
// 等同于
import { foo, bar } from'my_module';
// 虽然foo和bar在两个语句中加载,但是它们对应的是同一个my_module实例。也就是说,import语句是 Singleton 模式