ES6和CommomJs 的模块
在ES6之前,社区中有一些模块加载方案,主要有
CommonJS
和AMD
两种,前者用于服务器,后者用于浏览器。 ES6在语言层面上实现了模块功能,并且更加简单,可以成为服务端和浏览器端的通用解决方案。
ES6模块设计是尽量静态化的,在js代码编译时就能确定模块的依赖关系,
而 CommonJS
和 AMD
模块都是在代码运行时确定模块关系的。
看一个 CommonJS
的引入:
let {stat, exists, readFile} = require('fs')
上面的代码实质上是加载fs模块的所有方法,最终只用到三个方法,被称为“运行时加载”
ES6的模块加载就不一样,通过 export
命令显示指定输出代码,输入时也采用静命令的形式
import {stat, exists, readFile} = require('fs')
上面的代码实质是从fs模块中只加载 3 个方法,称为“编译时加载”
ES6可以在编译时就完成模块的编译,效率要比 CommonJS
方式高。
ES6模块的加载机制以 CommonJS
模块完全不同, CommonJS
输出的是一个值的浅拷贝,而ES6模块输出的是值的只读引用,是活的值。具体区别可以查看《ES6标准入门》P277。
ES6 模块
export 和 import 命令
可以使用 export
命令具名到处变量和方法,使用 import {xxx,xxx} from '...'
方式导入。
导出语句必须要写在文件按的顶层文字,就是不能被像 if(...) {export let name = 'mmm'}
代码块包含。
export
语句导出的值是动态的,绑定在其所在的模块。
// export.js
export let name = "ddxg" // 导出变量
export let age = 25
// 导出函数
export function showName(name) {
console.log(name)
}
// 导出类
export class Car {...}
// ---------------------
// 上面的导出可以改写成一次性导出的方式
let name = "ddxg" // 导出变量
let age = 25
export {name, age} // 代码更简洁
// ---------------------
// 导出时还可以使用 as 关键字重命名,还可一个面两重复导出
export {
name as lastName,
name as firstName, // name导出两次
age as myAge
}
导入,import
后面需要花括号,变量名需要跟导出时定义的变量名一致。
import
命令有提升效果,编译时会提升到整个模块的头部。
// index.js 引入
import {name, showName, Car} from './export' // 按需引入
// 可以使用 as关键字 重命名
import {name as lastName, showName, Car as MyCar} from './export'
// ---------------------
// 使用 * 关键字整体加载模块
import * as circle from './circle'
console.log('面积: ', circle.area(4))
console.log('周长: ', circle.circumference(4))
// ----------------------
// 使用module 命令也可以实现整体导入的效果
module circle from './circle'
console.log('面积: ', circle.area(4))
console.log('周长: ', circle.circumference(4))
// ---------------------
// 导入之后又马上导出
export {es6 as default} from 'module'
// 等价于:
import {es6} from 'module'
export default es6
// ----------------------
export {es6} from 'module'
// 等价于:
import {es6} from 'module'
export es6
// ---------------------
// 仅仅只是执行postErrot.js的代码
import 'postErrot'
export default 和 import 命令
export default
命令可以为模块指定默认输出,引入者自行定义命名,一个模块只能执行一次这个命令。
// test.js
// 默认输出的是一个匿名函数,导出非匿名函数也可,只是加载的时候视为匿名函数,没区别
export default function() {
console.log('ddd')
}
// ------------------------
// 也可写成
function foo () {
console.log('ddd')
}
export default foo
// -------------------------
// 导出class,portError.js
export default class {...}
导入的时候 import
后面不需要使用花括号,
import foo2 from './test.js'
foo2() // ddd
// 导入class
import PostError from './portError.js'
let postError = new PostError(...)
一个模块文件同时使用 默认导出 和 具名导出
// export.js
let name = 'ddd'
let age = 25
function showName(){...}
function showAge(){...}
export name
export age
export default {
showName,
showAge
}
// 引入,index.js
import fns, {name, age} from './export.js'
console.log('name', name)
console.log('age', age)
fns.showName()
fns.showAge()
模块的继承
// criclePlus.js
// 输出了cricle模块的所有属性和方法,export * 会忽略cricle模块的default导出。
export * from 'cricle'
// 又输出了新的e变量
export let e = 2.71828
// 输出默认导出
export default function(x) {
return Math.exp(x)
}
// 引入
// 引入cricle模块所有和新增e变量
module criclePlus from './criclePlus.js'
import exp from './criclePlus.js'
console.log(exp(criclePlue.e)) // exp(2.71828)
// 等价:
import * as criclePlus from './criclePlus.js'
CommonJS 模块
Node.js
使用 CommonJS
作为模块规范,每个文件就是一个模块,有自己的作用域。
CommonJS
规范规定,每个模块内部,module
变量代表当前模块。这个变量是一个对象,它的 exports
属性(即 module.exports
)是对外的接口。加载某个模块,其实是加载该模块的 module.exports
属性。
CommonJS
规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。
module.exports
module.exports
属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取 module.exports
变量。
简单的例子:
// test.js,导出
var x = 5;
var addX = function (value) {
return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
// --------------------------
// 导入
// 这个 example 其实就是 导出模块的 module.exports
var example = require('./test.js');
console.log(example.x); // 5
console.log(example.addX(1)); // 6
console.log(example)
// {
// x: 5,
// addX: [Function]
// }
直接给module.exports赋值导出:
// 导出一个对象,有add和show方法
function add() {...}
function show() {...}
module.exports = {
add,
show
}
// --------------------------------
// 导入
var example = require('./test.js');
example.add()
example.show()
exports变量
为了方便,Node为每个模块提供一个 exports
变量,指向 module.exports
。这等同在每个模块头部,有一行这样的命令 var exports = module.exports
。
exports.area = function (r) {
return Math.PI * r * r;
};
exports.circumference = function (r) {
return 2 * Math.PI * r;
};
// 导入逻辑是一样的
有个重要的问题是不能将 exports
重新赋值,因为这样等于切断了exports与module.exports的联系。就是可以使用 module.exports = xxx
,但是不能使用 exports = xxx