CommonJS对模块的规范
图说 CommonJS
1. 模块的引用
- 使用关键词
require
var math = require('math');
- 在文件中不要自己定义
require
, 这样会覆盖原有的require
//测试文件:require_show.js
console.log(require);
结果如下:
2. 何谓模块
分两大类:
- 核心模块 (比如: fs, http等node提供的原生模块)
- 文件模块 (用户自己定义的模块)
分三大类
- js 类型
- c/c++ 类型
- json 类型
node 中,一个文件就是一个模块,其中存在一个module对象,代表模块自身
//测试文件 module_show.js
console.log(module);
结果如下:
其中 path
属性存放的数组代表此模块可能存在的路径,当前文件所在路径为其中 filename
属性所存放的值,也就是数组的第一个,而且这个数组内值的排序就是node寻找指定模块地址的顺序,一层一层目录逐级查找
注意到其中还有一个 exports
属性存放的是一个空对象,这是模块机制的一个重要组成部分,再来一个例子,在之前的文件中的 exports
对象挂载一个方法:
//测试文件 module_show.js
module.exports.sayHi = function () {
console.log('this is module_show.js');
}
console.log(module);
结果如下:
发现 exports
多了一个方法,那么在另外一个(模块)文件中使用,该文件与之前文件在同一级目录:
//测试文件 module_use.js
const obj = require('./module_show.js');
// 测试 obj 是什么东西
console.log(obj);
// 测试是否可以调用sayHi方法
obj.sayHi();
//测试文件 module_show.js
module.exports.sayHi = function () {
console.log('this is module_show.js');
}
结果如下:
这说明前一个文件的 module.exports
所存放的对象通过 require
赋给了另一个文件中的 obj 变量
另一种情况,如果说 module_show.js 文件如下书写,会如何?
//测试文件 module_show.js
// 直接将一个方法赋给 module.exports
module.exports = function () {
console.log('this is module_show.js');
}
console.log(module);
结果如下
module.exports 现在已经不是object对象了,而成为了 Fucntion 类型,所以在另外一个文件中可以直接使用了
//测试文件 module_use.js
const obj = require('./module_show.js');
// 测试 obj 是什么东西
console.log(obj);
// 测试是否可以作为方法使用
obj();
//测试文件 module_show.js
module.exports = function () {
console.log('this is module_show.js');
}
结果如下:
也就是说,require
引入的是那个模块(文件)的 module.exports 的值,原始的 module.exports 为一个对象,可以对其添加属性与方法,也可以将一个全新类型的值赋给它。
说白了,exports 就是一个初始化为 { } 的对象,为module的一个属性,可以被引用到它的别的模块访问,你可以改变其内容,仅此而已
3. 模块标识
- 定义:传给 require() 方法的参数
- 内容:
- 核心模块 : http, fs, path
- . 或 .. 开始的相对路径文件模块
- 以 / 开始的绝对路径模块
- 非路径形式的文件模块
一个模块的真面目
写一个简单的 web 服务器,向页面中写一个 ‘Hello world’
// module_architect.js
const http = require('http');
http.createServer(function (req, res) {
res.statusCode = 200;
res.write('Hello world');
res.end();
}).listen(4000);
使用 chrome 调试模块
node --inspect module_architect.js
看看在chrome中此文件如何显示的:
此模块被封装在一个函数中,而传入函数的参数有 exports
,require
, module
, __filename
, __dirname
好了,出现了新的问题,exports
与 module.export
是什么关系,后面的两个变量又是啥,书写一下模块来看一下
//测试文件 module_show.js
//为 module.exports 挂载属性 a 并赋值
module.exports.a = 1;
console.log(module.exports);
console.log(exports);
console.log('.......');
//为 exports 挂载属性 b 并赋值,同时改变 a 的值
exports.a = 2;
exports.b = 2;
console.log(module.exports);
console.log(exports);
console.log('.......');
// 查看两个变量的值
console.log(__filename);
console.log(__dirname);
结果如下
- 结论一:
module.exports
和exports
是相同的 - 结论二:
__filename
显示当前文件所在的绝对路径,__dirname
显示当前文件所在文件夹的绝对路径
模块的优势
从上面可以看出,一个模块会被node封装在一个函数中,形成一个闭包,所以可以防止变量污染啦
模块引用的实现
- 核心模块:路径分析 -> 引入
- 文件模块:路径分析 -> 文件定位 -> 编译执行
- 一个 c/c++参与编写的node模块示意图
c/c++编写node模块(此处略去…)
我觉得我的水平还没到这个地步,而且书中篇幅也比较多,所以就跳过了≥.≤
包(package)
包的组织形式
不多说,上图
包规范(来自CommonJS)
- package.json 包描述文件
- bin 用于存放可执行二进制文件的目录
- lib 用于存放js代码目录
- doc 用于存放文档目录
- test 用于存放单元测试用例的代码