浅谈Node模块系统及其模式(2)

上面的函数模拟了Nodejs原生用来加载模块的require函数的行为,当然,它只是具有一个雏形,而没有完全准确的反映真实的require函数的行为,但是它可以让我们很好的理解Node模块系统的内部机制,一个模块怎么被定义和被夹在,我们的自制模块系统具备下面的功能

  1. 模块名被作为参数传入,首先要做的事情时调用require.resolve方法根据传入的模块名生成module id(通过指定的resolve算法来生成)
  2. 如果该模块已经被加载过了,那么直接会从缓存中获得
  3. 如果该模块还没有被加载过,我们会初始化一个module对象,其中包含两个属性,一个是module id,另外一个属性是exports,它的初始值为一个空对象,该属性会被用于保存模块的export的公共的API代码
  4. 将该module进行cache
  5. 调用我们上面定义的loadModule函数来获取模块的源代码,将初始化的module对象作为参数传入,因为module是对象,引用类型,所以模块可以利用module.exports或者是替换module.exports来暴露它的公共API
  6. 最后,返回给调用者module.exports的内容,也就是该模块的公共API

看到这里,我们会发现,其实在Node 模块系统没有想象中的那么难,真正的技巧在于将模块的代码进行包装,以及创建一个运行时的虚拟环境。

定义一个模块

通过观察我们自制的require()函数的工作机制,我们应该很清楚的知道如何定义一个模块

const dependency = require('./anotherModule');

function log() {
 console.log(`get another ${dependency.username}`);
}

module.exports.run = () => {
 log();
}


// anotherModule.js

module.exports = {
 username: 'wingerwang'
}

最重要的是要记住在模块里面,除了被分配给module.exports的变量,其他的都是该模块私有的,在使用require()加载后,这些变量的内容将会被缓存并返回。

定义全局变量

即使所有的变量和函数都在模块本身的作用域内声明的,但是仍然可以定义全局变量,事实上,模块系统暴露一个用来定义全局变量的特殊变量global,任何分配到这个变量的变量都会自动的变成全局变量

需要注意的是,污染全局作用域是一个很不好的事情,甚至使得让模块系统的优点消失,所以只有当你自己知道你要做什么时候,才去使用它

module.exports VS exports

很多不熟悉Node的开发同学,会对于module.exports和exports非常的困惑,通过上面的代码我们很直观的明白,exports只是module.exports的一个引用,而且在模块加载之前它本质上只是一个简单的对象

这意味着我们可以将新属性挂载到exports引用上

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/656.html