Node.js 使用 CommonJS 模块系统,而在浏览器中,则还正在实现 ES 模块标准。

在实践中,这意味着在 Node.js 中使用 require() ,而在浏览器中则使用 import

遵循的规范

1、require 是 AMD规范引入方式。

2、import是es6的一个语法标准,如果要兼容浏览器的话必须转化成es5的语法。

调用的时间

1、require是运行时调用,所以require理论上可以运用在代码的任何地方。

2、import是编译时调用,最好把 import 放在代码的开头。

解释器在执行语句时,遵循作用域原则。因为这和作用域有关系,如果在顶层导入模块,此时它的作用域是全局的。如果在函数内部导入了模块,那它的作用域只是局部的,不能被其它函数使用。如果其它函数也要用到这个模块,还需要再次导入比较麻烦。在用 import 语句导入模块时最好按照这样的顺序:python 标准库模块 -》python 第三方模块 -》自定义模块。

本质

require是赋值过程,其实require的结果就是对象、数字、字符串、函数等,再把require的结果赋值给某个变量。

import是解构过程,但是目前所有的引擎都还没有实现import,我们在node中使用babel支持ES6,也仅仅是将ES6转码为ES5再执行,import语法会被转码为require。

exports和module.exports

exports方式使用方法

1
exports.[function name] = [function name]

module.exports方式使用方法

1
moudle.exports= [function name]

根本区别

为了方便,Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令。

1
var exports = module.exports;

module和exports是Node.js给每个js文件内置的两个对象。

1
2
console.log(exports); //输出:{}
console.log(module);  //输出:Module {..., exports: {}, ...} (注:...代表省略了其他一些属性)

module.exports和exports一开始都是一个空对象{},实际上,这两个对象指向同一块内存。这也就是说module.exports和exports是等价的(有个前提:不去改变它们指向的内存地址)。

例如:exports.age = 18和module.export.age = 18,这两种写法是一致的(都相当于给最初的空对象{}添加了一个属性,通过require得到的就是{age: 18})。

require引入的对象本质上是module.exports。这就产生了一个问题,当 module.exports和exports指向的不是同一块内存时,exports的内容就会失效。

1
2
module.exports = {name: '萤火虫叔叔'}
exports = {name: '萤火虫老阿姨'}

于是我们可以直接在 exports 对象上添加方法,表示对外输出的接口,如同在module.exports上添加一样。

注意,因为 Node 模块是通过 module.exports 导出,如果直接将exports变量指向一个值,就切断了exports与module.exports的联系,导致意外发生:

1
2
3
4
// a.js
exports = function a() {};
// b.js
const a = require('./a.js') // a 是一个空对象