模块化机制
Javascript一直没有模块体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。这对开发大型的、复杂的项目形成了巨大障碍。
在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
1)CommenJS 模块化规范()
CommonJS 和 AMD 模块,都只能在运行时确定模块之间的依赖关系,以及输入输出的变量。比如,CommonJS 模块就是对象,输入时必须查找对象属性。
// CommonJS模块
let { stat, exists, readfile } = require('fs');
// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。
Node内部提供一个Module构建函数。所有模块都是Module的实例。每个模块内部,都有一个module对象,代表当前模块。它有以下属性。
module.id 模块的识别符,通常是带有绝对路径的模块文件名。
module.filename 模块的文件名,带有绝对路径。
module.loaded 返回一个布尔值,表示模块是否已经完成加载。
module.parent 返回一个对象,表示调用该模块的模块。
module.children 返回一个数组,表示该模块要用到的其他模块。
module.exports 表示模块对外输出的值。
为了方便,Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令,。
var exports = module.exports; 。
Require函数是nodejs提供的内置函数,用于加载指定路径的模块或者是指定名称的模块。将加载的模块进行返回。
var path = require('fs');
导入
let { firstName, lastName } = require('./1-module.js')
等同于
let obj = require('./1-module.js')
let first = obj.firstName;
let last = obj.lastName;
对象解构(模式匹配)
let data = {
username: '张三',
age: 12
}
let { username, age } = data
导出
const firstName = 'wu';
const lastName = 'yifan';
// module.export = {
// firstName: firstName,
// lastName: lastName
// }
// 对象的简写形式
module.exports = {
firstName,
lastName
}
2)AMD 模块化规范
3)ES6 模块化规范
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。
ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";。
导入
静态的import 语句用于导入由另一个模块导出的绑定。
import * as person from './person.js' //导入整个模块内容
import {firstName,lastName} from './person.js' //导入多个接口
import {firstName as name} from './person.js' //重命名
import '/modules/my-module.js'; //运行整个模块而不导入任何值
import myDefault from './my-module.js'; // 导入使用export default导出的模块
导出
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系,也就是说外部接口需要用这个接口名来引用。
var firstName = 'Michael';
var lastName = 'vicky';
export { firstName, lastName }; //列表导出
export { firstName as first, lastName as last}; //重命名导出
export var a = 3; //导出单个属性
export function multiply(x, y) { return x * y; }; //导出单个属性
//默认导出,一个模块只能有一个默认导出,不能使用 var、let 或 const 用于导出默认值 export default。
export default {}
export default function foo(){}
var a = 1;
export a; //报错,因为没有提供对外的接口。应该export var a = 1; 或者export {a}
ES6模块与CommonJS模块的差异:
1、CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
ES6 模块加载的不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
ES6 模块输出的是值的引用。
2、CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
加载的是一个对象,即module.exports属性,该对象只有在脚本运行完才能生成。
CommonJS 模块输出的是值的拷贝