C++Node.js入门:模块机制

CommonJS规范 

   
早以Netscape诞生快继,JavaScript就直以探索本地编程的里程,Rhino是其表示产物。无奈那时服务端JavaScript走之程皆是参照众多劳动器端语言来贯彻之,在这么的背景之下,一没有特色,二从未实用价值。但是就JavaScript在前者的应用更加宽广,以及劳动端JavaScript的推,JavaScript现有的正规化好脆弱,不便宜JavaScript大规模的运用。那些以JavaScript为宿主语言的条件遭到,只有自己的根基原生对象以及档次,更多的靶子和API都取决于宿主的供,所以,我们得望JavaScript缺少这些意义:

  • JavaScript没有模块系统。没有原生的支持密闭作用域或因管理。 
  • JavaScript没有标准库。除了片为主库外,没有文件系统的API,没有IO流API等。 
  • JavaScript没有标准接口。没有如Web Server或者数据库的汇合接口。 
  • JavaScript没有保险管理网。不能自动加载与装置依赖。 

   
于是便发生了CommonJS(http://www.commonjs.org)规范的出现,其目标是为了构建JavaScript在包括Web服务器,桌面,命令行工具,及浏览器方面的生态系统。CommonJS制定了解决这些问题的一些规范,而Node.js就是这些规范的一种实现。Node.js自身实现了require方法作为其引入模块的方法,同时NPM也基于CommonJS定义的包规范,实现了依赖管理和模块自动安装等功能。这里我们将深入一下Node.js的require机制和NPM基于包规范的应用。

 

简短模块定义和利用

   
在Node.js中,定义一个模块十分有利于。我们盖计算圆形的面积以及周长两单道呢条例,来表现Node.js中模块的定义方式。

1 var PI = Math.PI; 
2 exports.area = function (r) {
3  return PI * r * r; 
4 }; 
5 exports.circumference = function (r) {
6  return 2 * PI * r; 
7 };

   
将此文件存为circle.js,并新建一个app.js文件,并形容副以下代码:

1 var circle = require('./circle.js'); 
2 console.log( 'The area of a circle of radius 4 is ' + circle.area(4));

   
可以见到模块调用也酷好,只需要require需要调用的公文即可。

   
在require了这文件后,定义在exports对象上之点子就好擅自调用。Node.js将模块的概念及调用都打包得最简单好,从API对用户自己这一个角度来说,Node.js的模块机制是老大精良的。

 

模块载入策略

   
Node.js的模块分为两近似,一近似为原生(核心)模块,一接近为文件模块。原生模块于Node.js源代码编译的时刻编译进了亚向前制实行文书,加载的进度极其抢。另一样像样公事模块是动态加载的,加载速度比原生模块慢。但是Node.js对原生模块和文书模块都进行了缓存,于是当次糟require时,是休见面发生再支付的。其中原生模块都叫定义在lib这个目录下面,文件模块则不定性。

node app.js

   
由于通过命令行加载启动之公文几乎都为文件模块。我们由Node.js如何加载文件模块开始称起。加载文件模块的办事,主要由于原生模块module来兑现与到位,该原生模块于开行时既让加载,进程一直调用到runMain静态方法。

1 // bootstrap main module. 
2 Module.runMain = function () {
3     // Load the main module--the command line argument. 
4     Module._load(process.argv[1], null, true); 
5 };

   
_load静态方法在分析文件称后执行

var module = new Module(id, parent);

    并因文件路径缓存当前模块对象,该模块实例对象则因文件名加载。

module.load(filename);

   
实际上在文件模块中,又分为3类模块。这三类公事模块然后缀来区别,Node.js会根据后缀名来决定加载方法。

  • .js。通过fs模块并读取js文件并编译执行。 
  • .node。通过C/C++进行编辑的Addon。通过dlopen方法开展加载。 
  • .json。读取文件,调用JSON.parse解析加载。

   
这里我们用详细描述js后缀的编译过程。Node.js在编译js文件之经过被其实完成的步调有对js文件内容展开头尾包装。

    以app.js为条例,包装下的app.js将会化以下形式:

1 (function (exports, require, module, __filename, __dirname) { 
2     var circle = require('./circle.js');
3     console.log('The area of a circle of radius 4 is ' + circle.area(4)); 
4 });

   
这段代码会通过vm原生模块的runInThisContext方法执行(类似eval,只是有显著上下文,不招全局),返回吗一个具体的function对象。最后传module对象的exports,require方法,module,文件称,目录名作为实参并尽。

    这就是干吗require并没定义在app.js
文件中,但是这个措施可存在的案由。从Node.js的API文档中可见到还有__filename、__dirname、module、exports几单无概念但是也在的变量。其中__filename和__dirname在查找文件路径的进程被分析得到后传入的。module变量是此模块对象自我,exports是在module的构造函数中初始化的一个缺损对象({},而无是null)。

   
在是主文件中,可以通过require方法去引入其余的模块。而实际是require方法其实调用的尽管是load方法。

   
load方法以载入、编译、缓存了module后,返回module的exports对象。这就是circle.js文件被只有定义在exports对象及的艺术才能够叫外表调用的故。

    以上所描述的模块载入机制都定义在lib/module.js中。