浅谈Node模块系统及其模式

模块是构建应用程序的基础,也使得函数和变量私有化,不直接对外暴露出来,接下来我们就要介绍Node的模块化系统和它最常用的模式

为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。

模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。

module的本质

我们都知道,JavaScript有一个很大的缺陷就是缺少namespacing的概念,程序运行在全局作用域下,很容易被内部应用程序的代码或者是第三方依赖程序的数据所污染,一个很典型的解决方案就使通过IIFE来解决,本质上是利用闭包来解决

const module = (() => {
 const privateOne = () => {
  // ... 
 }

 const privateTwo = () => {
  // ... 
 }

 const exported = {
  publicOne: () => {
   // ...
  },
  publicTwo: []
 }

 return exported;
})()

console.log(module);

通过上面的代码,我们可以看出,module变量包含的只有对外暴露的API,然而剩下的module内容是对外不可见的,而这个也是Node module system最核心的思想。

Node modules 说明

CommonJS是一个致力于将JavaScript生态系统标准化的一个组织,它最出名的一个提议就是我们众所周知的CommonJS modules,Node在本规范的基础上构建了他自己的模块系统,并且添加了一些自定义扩展,为了描述它是怎么工作的,我们可以使用上面所提到的module的本质的思想,自己做一个类似的实现。

自制一个module loader

下面的代码主要是模仿Node原始的require()函数的功能

首先,我们创建一个函数用来加载一个module的内容,将它包裹在一个私有的作用域中

function loadModule(filename, module, require) {
 const warppedSrc = `(function(module, mexports, require) {
  ${fs.readFileSync(filename, 'utf-8')}
 })(module, module.exports, require)`

 eval(warppedSrc);
}

module的源代码被包装到一个函数中,如同IIFE那样,这里的区别在于我们传递了一些变量给module,特指module、module.exports和require,注意的是我们的exports变量实质上是又module.exports初始化的,我们接下来会继续讨论这个

*在这个例子中,需要注意的是,我们使用了类似eval()或者是node的vm模块,它们可能会导致一些代码注入攻击的安全性问题,所以我们需要特别注意和避免

接下来,让我们通过实现我们的require()函数,来看看这些变量怎么被引入的

const require = (moduleName) => {
 console.log(`Required invoked for module: ${moduleName}`);
 const id = require.resolve(moduleName);
 if(require.cache[id]) {
  return require.cache[id].exports;
 }

 // module structure data
 const module = {
  exports: {},
  id: id
 }

 // uodate cache
 require.cache[id] = module;

 // load the module
 loadModule(id, module, require);

 // return exported variables
 return module.exports;
}

require.cache = {};
require.resolve = (moduleName) => {
 // resolve a full module id from the moduleName
}


      

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

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