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 };</pre>
}//迎接到场全栈开辟交换圈一同进修交换:582735936
]//面向1-3年前端职员
}   //协助打破手艺瓶颈,提拔思维能力

将这个文件存为circle.js,并新建一个app.js文件,并写入以下代码:

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

能够看到模块挪用也异常轻易,只须要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 };</pre>

_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 });</pre>

这段代码会经由历程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中。

    原文作者:前端爱好者
    原文地址: https://segmentfault.com/a/1190000017194039
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞