Node -- module

197 阅读3分钟

1.在CommonJs规范中:

  • 一个文件就是一个模块,拥有单独的作用域;
  • 普通方式定义的变量、函数、对象都属于该模块内;
  • 通过require来加载模块;
  • 通过exports和modul.exports来暴露模块中的内容;

2.模块加载机制

  • require(同步加载)基本功能:读取并执行一个JS文件,然后返回该模块的exports对象,如果没有发现指定模块会报错;
  1. 核心模块(位于Node的系统安装目录中)。
  2. 如果是一个目录,那么require会先查看该目录的package.json文件,然后加载main字段指定的脚本文件。否则取不到main字段,则会加载index文件如果指定的模块文件没有发现,Node会尝试为文件名添加.js、.json、.node后,再去搜索。.js文件会以文本格式的JavaScript脚本文件解析,.json文件会以JSON格式的文本文件解析,.node文件会议编译后二进制文件解析。
  3. 如果不是核心模块也不是一个目录
    1. 当前文件目录下的node_modules目录下;
    2. 没有符合条件的则去当前目录的父文件夹的node_modules目录下;
    3. 若没有则再往上一层目录的node_modules目录下;
    4. 若没有则重复3,直到找到符合的模块或者根目录为止。
  4. 如果想得到require命令加载的确切文件名,使用require.resolve()方法。

3.模块的循环加载

  • 模块可以多次加载,但只会在第一次加载的时候运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果;模块的加载顺序,按照代码的出现顺序是同步加载的; 如果发生模块的循环加载,即A加载B,B又加载A,则B将加载A的不完整版本。
// a.js
exports.x = 'a1';
console.log('a.js ', require('./b.js').x);
exports.x = 'a2';

// b.js
exports.x = 'b1';
console.log('b.js ', require('./a.js').x);
exports.x = 'b2';

// main.js
console.log('main.js ', require('./a.js').x);
console.log('main.js ', require('./b.js').x);

上面代码是三个JavaScript文件。其中,a.js加载了b.js,而b.js又加载a.js。这时,Node返回a.js的不完整版本,所以执行结果如下。

$ node main.js
b.js  a1
a.js  b2
main.js  a2
main.js  b2

修改main.js,再次加载a.js和b.js。

// main.js
console.log('main.js ', require('./a.js').x);
console.log('main.js ', require('./b.js').x);
console.log('main.js ', require('./a.js').x);
console.log('main.js ', require('./b.js').x);

执行上面代码,结果如下。

$ node main.js
b.js  a1
a.js  b2
main.js  a2
main.js  b2
main.js  a2
main.js  b2
  • 上面代码中,第二次加载a.js和b.js时,会直接从缓存读取exports属性,所以a.js和b.js内部的console.log语句都不会执行了。

4.模块封装器

(function(exports, require, module, __filename, __dirname) {
// 模块的代码实际上在这里
});

5.模块作用域

  • __dirname:当前模块的目录名
  • __filename:当前模块的文件名。 这是当前的模块文件的绝对路径(符号链接会被解析)。
  • exports
  • module
  • require(id)

6.require对象

require.cache 被引入的模块将被缓存在这个对象中 require.main Module 对象,表示当 Node.js 进程启动时加载的入口脚本 require.resolve(request [,options]) 使用内部的 require() 机制查询模块的位置,此操作只返回解析后的文件名,不会加载该模块 require.resolve.paths(request)

7.module 对象

  • 每个模块都有一个module变量,该变量指向当前模块。module不是全局变量,而是每个模块都有的本地变量。所有代码都运行在模块作用域,不会污染全局作用域;

  • module.id模块的识别符,通常是带有绝对路径的模块文件名。

  • module.filename 模块的文件名。

  • module.loaded 返回一个布尔值,表示模块是否已经完成加载。

  • module.parent 返回一个对象,表示调用该模块的模块。

  • module.children 返回一个数组,表示该模块要用到的其他模块。

  • module.exportsexports:为了方便,node为每个模块提供一个exports变量,其指向module.exports,相当于在模块头部加了这句话:var exports = module.exports,在对外输出时,可以给exports对象添加方法,PS:不能直接赋值(因为这样就切断了exports和module.exports的联系);

  • module.paths:模块的搜索路径。

  • module.require(id):module.require() 方法提供了一种加载模块的方法,就像从原始模块调用 require() 一样。