Node应用有采用CommonJS规范的模块组成,据此每个文件就是一模块,有自己的作用域。其内定义的 变量,函数,类都是私有的,对其它文件不可见。
// example.js
var x = 5;
var addX = function (value) {
return value + x;
};
//分享变量
global.warning = true;//不推荐
CommonJS规定,每个模块内部module变量代表当前模块,其exports属性是对外接口,加载某个模块即是加载其module.exports 属性。特点如下:
var x = 5;
var addX = function (value) {
return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
var example = require('./example.js');
console.log(example.x); // 5
console.log(example.addX(1)); // 6
Node内部提供一个Module构造函数,所有模块都是Module的实例;每个模块内部都有一个代表当前模块的module 对象
- module.exports属性:表示当前模块对外输出(module.exports变量)的接口
var EventEmitter = require(‘events’).EventEmitter; module.exports = new EventEmitter();
setTimeout(function() { module.exports.emit(‘ready’); }, 1000); //也可以写成 var a = require(‘./a’); a.on(‘ready’, function() { console.log(‘module a is ready’); });
- exports变量: Node为每个模块提供一个指向module.exports的exports变量,等同于在每个模块头部加入
```var exports=module.exports```
### AMD规范与CommonJS规范的兼容性
> CommonJS规范加载模块是同步的,AMD规范则是非同步加载模块,允许指定回调函数。
因Node主要用于服务器编程,模块文件一般已经存于本地硬盘,故加载起来较快,无需考虑非同步加载方式,
所以较适用CommonJS规范。若是浏览器环境,则一般考虑AMD规范。
//AMD 使用define定义模块 define([‘package/lib’], function(lib){ function foo(){ lib.log(‘hello world!’); } return { foo: foo }; }); //兼容CommonJS规范时写成。。。 define(function (require, exports, module){ var someModule = require(“someModule”); var anotherModule = require(“anotherModule”);
someModule.doTehAwesome(); anotherModule.doMoarAwesome();
exports.asplode = function (){ someModule.doTehAwesome(); anotherModule.doMoarAwesome(); }; });
### require命令
- 基本用法
> Node使用CommonJS模块规范,内置require命令加载文件。其基本功能是读入并执行一个js文件
,然后返回该模块的exports对象,无则报错。
- 加载规则
> require命令用于加载文件,默认后缀.js
var foo = require(‘foo’); // 等同于 var foo = require(‘foo.js’);
根据参数不同,require命令去不同路径寻找模块文件
1. '/':加载的是一个位于绝对路径的模块文件```require('/home/marco/foo.js') ```
2. './':加载相对路径文件
3. '':加载默认提供的核心模块(Node系统安装目录中)或者位于各级node_modules目录的已安装模块。
旨在使不同的模块可以将所依赖的模块本地化。
4. 'example-module/path/to/file':从前到后加载路径
5. 若指定模块文件未发现则尝试为文件名添加.js,.json,.node后再去搜索
6. 若想得到require命令加载的文件的确切名使用```require.resolve()方法
- 目录加载规则
> 为便于组织我们常把相关文件放于一个目录里边,此时最好为该目录设置一个入口文件,让require
方法可以据此加载整个目录。
// package.json { “name” : “some-library”, “main” : “./lib/some-library.js” }
require发现参数字符串指向一个目录以后,会自动查看该目录的package.json文件,然后加载main字段指定的入口文件。
如果package.json文件没有main字段,或者根本就没有package.json文件,则会加载该目录下的index.js文件或index.node文件。
- 模块缓存
> 首次加载某模块时,Node会缓存;而后加载便直接从缓存读取该模块的module.exports属性
- 环境变量NODE_PATH
> Node执行一个脚本时,会先查看其环境变量(以冒号分割的绝对路径)。在其它位置找不到指定模块时,Node会
去这些路径查找。
- 模块的循环加载
>第二次加载a.js和b.js时,会直接从缓存读取exports属性,所以a.js和b.js内部的console.log语句都不会执行了。
- require.main
> 判断模块是直接执行还是调用执行,直接执行则```require.main===module```
### 模块的加载机制
> CommonJS模块的加载机制是:输入的是被输出值的拷贝。
即一旦输出,一个值模块内部的变化就影响不到该值。
//lib.js var c = 3; function add() {return c++;} module.exports = {c,add,} //main.js var c = require(‘./lib’).c; var add = require(‘./lib’).add; console.log(c); add(); console.log(add()); console.log(c);//3 4 3
- require的内部处理流程
> require在CommonJS中用于加载其它模块的命令。其实不是一全局命令,而是指向当前模块的
调用了Node内部命令Module._load的module.require命令。
Module._load = function(request, parent, isMain) { // 1. 检查 Module._cache,是否缓存之中有指定模块 // 2. 如果缓存之中没有,就创建一个新的Module实例 //require(): 加载外部模块 //require.resolve():将模块名解析到一个绝对路径 //require.main:指向主模块 //require.cache:指向所有缓存的模块 //require.extensions:根据文件的后缀名,调用不同的执行函数 // 3. 将它保存到缓存 // 4. 使用 module.load() 加载指定的模块文件, // 读取文件内容之后,使用 module.compile() 执行文件代码 // 5. 如果加载/解析过程报错,就从缓存删除该模块 // 6. 返回该模块的 module.exports }; Module.prototype._compile = function(content, filename) { // 1. 生成一个require函数,指向module.require // 2. 加载其他辅助方法到require // 3. 将文件内容放到一个函数之中,该函数可调用 require // 4. 执行该函数 };
(function (exports, require, module, __filename, __dirname) { // YOUR CODE INJECTED HERE! }); /*一旦require函数准备完毕,整个所要加载的脚本被放到一个新函数中以避免全局污染。 Module._compile方法是同步执行的,所以Module._load要等它执行完成,才会向用户返回module.exports的值。 */ ```