模块特性
具有模块作用域,模块中没有暴露导出的变量或对象无法被外部访问。模块内的代码被执行,产生实例,便会独立运行下去,直到模块代码运行结束,所有操作都在模块内部上下文运行。
// 调用者
const run = require('./running')
run.start()
setTimeout(() => {
run.reset()
}, 6000)
// 被调用者
var cnt = 0
var clockId = null
function start() {
clockId = setInterval(() => {
console.log(++cnt);
}, 1000)
}
function reset() {
clearInterval(clockId)
}
module.exports = {
start,
reset
}
会一直执行计时器,直到被调用reset
模块加载
执行命令require时,就会加载一次模块内的所有代码,并返回模块对象。模块具有三种类型:
// 系统内置模块
const http = require('http')
// 自建模块
const my_module = require('./my_module')
// 第三方模块
const monent = require('monent')
模块在第一次加载后会被缓存,多次调用require不会导致模块的代码被多次执行。无论那种模块,都会先从缓存中加载。
// 子模块 sub.js
var shared = { a: 1 }
module.exports = { shared }
// 调用者
let run1 = require('./sub')
let run2 = require('./sub')
run1.shared === run2.shared // true
模块加载也有优先级,其中内置模块优先级最高,第三方模块优先级低。自定义模块不参与比较,因为自定义模块采用路径引用,不会发生冲突。
对于自定义模块模糊查询(省略后缀),查找文件顺序依次是 xxx - xxx.js - xxx.json -xxx.node
采用目录加载方式,先查找package.json中的main属性入口,如果没有找到,则加载目录价index.js文件,若仍然失败,打印错误。
|-module
|-package.json
|-a.js
// package.json
{
"main": "./a.js"
}
// 调用者
require('./module')
模块导出
每个js模块都有一个module对象,该对象有个exports属性,即是暴露的模块内容。
Module {
id: '.',
path: 'R:\\Node\\module',
exports: { start: [Function: start], reset: [Function: reset] },
parent: null,
filename: 'R:\\Node\\module\\running.js',
loaded: false,
children: [],
paths: [
'R:\\Node\\module\\node_modules',
'R:\\Node\\node_modules',
'R:\\node_modules'
]
}
而调用模块的变量其实就指向exports,因此我们有两种模块导出方式
// 方法一:全覆盖
module.exports = {
XXX: XXX,
YYY: YYY
}
// 方法二:逐个设置
module.exports.XXX = XXX
module.exports.YYY = YYY
为了方便编码,node提供了一个新的对象exports,指向的也是module.exports,只要不采用全覆盖的方式导出变量(改变了指向),两者就指向相同对象。
无论如何改变,导出时的对象只锁定为module.exports,require只识别module.exports