一、前言:
写这个,单纯是因为对别人写的不太满意,反正自己看,写着玩
二、两中规范
JavaScript模块化的出现可以避免全局变量污染、数据被破坏的问题,并且支持模块与模块之间相互依赖。
CommonJS 规范:
Node.js 应用由模块组成,每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。
每个模块内部有两个变量可以使用,require
和 module
。
require
用来加载某个模块module
代表当前模块,是一个对象,保存了当前模块的信息。exports
是module
上的一个属性,保存了当前模块要导出的接口或者变量
module.exports
// tempData.js
const obj = {
name: 'Libai',
age: '20'
};
module.exports = {
obj
};
引用
let tempData = require('./tempData.js');
console.log(tempData);
// {
// obj: {
// name: 'Libai',
// age: '20'
// }
// }
注意:
1、Node.js 在实现时,按照 CommonJS 规范,为每个模块提供一个 exports
的私有变量,指向 module.exports
,即:
var exports = module.exports
因此,上面暴露示例代码可以写成
// tempData.js
const obj = {
name: 'Libai',
age: 20
};
exports.obj = obj;
2、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 个方法。
ES6规范
ES6 模块不是对象,而是通过export
命令显式指定输出的代码,再通过import
命令引入。
这句话怎么理解呢?后面再说
export
命令用于规定模块的对外接口或者变量,import
命令用于引入其他模块提供的功能。ES6的模块暴露又有两个命令:
export和export default,下面说一下他们的区别
export
// tempData.js
// 写法一:声明的同时暴露
export const name = "Libai";
export const age = "20";
export function sayHi() {
console.log("Hello world");
}
// 写法二:先声明后暴露
// 推荐这种写法
const name = "Libai";
const age = "20";
function sayHi() {
console.log("Hello world");
}
export {
name,
age,
sayHi
};
// 写法三:先声明后暴露
// 使用别名
const name = "Libai";
const age = "20";
function sayHi() {
console.log("Hello world");
}
export {
name as a,
age as b,
sayHi as c
};
注意以下暴露写法是错误的
const name = "Libai";
const age = "20";
function sayHi() {
console.log("Hello world");
}
export name;
export age;
export sayHi;
按照阮一峰《ECMAScript 6 入门》给出的解释:
export
命令规定的是对外的接口,必须与模块内部的变量或者函数建立一一对应关系,而上面的写法仅仅是直接暴露的是变量或者函数,并没有建立对应关系。
那为什么这种写法是可以建立对应关系的呢?
const name = "Libai";
const age = "20";
function sayHi() {
console.log("Hello world");
}
export {
name,
age,
sayHi
};
一开始我认为,基于要建立对应关系这个原则,实际上是这样要求的:
const name = "Libai";
const age = "20";
function sayHi() {
console.log("Hello world");
}
export {
name: name,
age: age,
sayHi: sayHi
};
只是在es6语法的支持下可以缩写成:
const name = "Libai";
const age = "20";
function sayHi() {
console.log("Hello world");
}
export {
name,
age,
sayHi
};
但事实上并不是这样。
export { }并不是暴露一个对象,{}只是个语法糖,仅仅是个格式,想要暴露哪个变量或者函数,就放在{}里面。
也就是说如果要使用export来暴露模块内部的东西,需要遵循它本身的语法要求。以上结论暂时保留。
引用
// 解构引入
import { name, age, sayHi } from './tempData.js';
// 通配符引入
import * as tempData from './tempData.js';
console.log(tempData);
// {
// age: "20"
// name: "Libai"
// sayHi: ƒ sayHi()
// }
// 使用别名
// 注意一旦使用了别名,后续只能使用别名
import {
name as n,
age as a,
sayHi
} from './tempData.js';
console.log(n);
console.log(a);
注意:如果在export时就已经使用了别名,那么import时使用解构只能使用别名
// export
const name = "Libai";
const age = "20";
function sayHi() {
console.log("Hello world");
};
export {
name as a,
age as b,
sayHi
};
// import
// 注意引入是同样也可以使用别名
import {
a,
b,
sayHi
} from './tempData.js';
export default
上面的示例可以发现一个问题:
如果我想引入某个模块里面的东西,前提是得知道模块对外暴露了什么?这样的设计就要求使用者需要事先阅读模块的对外暴露。
为了解决这个问题,ES6规范提供了export default用来指定模块默认暴露。
一个模块只能有一个默认暴露,因此export default
命令只能使用一次,export default
后面可以跟任意的变量或者函数,相当你把这个变量或者函数赋值给default,这一点很重要
// 合法
export default 42;
// 合法
const name = "Libai";
export default name;
// 合法
function sayHi() {
console.log("Hello world");
};
export default sayHi;
// 或者
export default function sayHi() {
console.log("Hello world");
};
// 支持匿名函数
export default function () {
console.log("Hello world");
};
// 合法
const name = "Libai";
const age = "20";
function sayHi() {
console.log("Hello world");
};
export default {
name,
age,
sayHi
};
引用
在import时不允许使用解构来获取,因为export defualt暴露的是一个整体
通常也不使用通配符,因为export default后面可以跟任意的变量或者函数,相当你把这个变量或者函数赋值给default,你会发现
// export default
const name = "Libai";
const age = "20";
function sayHi() {
console.log("Hello world");
};
export default {
name,
age,
sayHi
};
// import
import * as data from './tempData.js';
console.log(data);
// {
// default: {
// age: "20"
// name: "Libai"
// sayHi: ƒ sayHi()
// }
// }
正确的引入写法是
import tempData from './tempData.js';
console.log(tempData);
// {
// age: "20"
// name: "Libai"
// sayHi: ƒ sayHi()
// }