从Node.js看模块与包(一)

模块(Module)和包(Package)是Node.js最重要的支柱。开发一个具有一定规模的程序不可能只用一个文件,通常需要把各个功能拆分,封装,然后组合起来,模块正是为了实现这种方式而诞生的。在浏览器Javascript中,脚本模块的拆分和组合通常使用HTML的script标签来实现。Node.js提供了require函数来调用其他模块,而且模块都是基于文件的,机制十分简单。

Node.js中的模块和包机制的实现参照了CommonJS的标准,但并未完全遵循,不过两者的区别不大,一般来说你大可不必担心,只有当你试图制作一个除了支持Node.js之外还要支持其他平台模块或者包的时候才需要研究。通常两者没有直接冲突的地方。

通常会把Node.js的包和模块相提并论,因为模块和包没有本质的区别,两个概念也时常混用,如果要辨析,可以把包理解成实现了某个功能模块的集合,用于发布和维护。对使用者来说,模块和包的区别是透明的,因此经常不做区分。

什么是模块?

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

例如:var http = require('http')  

其中http是Node.js的一个核心模块,其内部是用C++实现的,外部用JS封装。我们 通过require函数获取了这个模块,然后才能使用其中的对象。

模块的创建与加载

Node.js中模块的创建十分简单,因为一个文件就是一个模块,要关注的问题就是如何在其他文件中获取这个模块,Node.js中提供了express 和 require两个对象,其中exports是模块公开的额接口,require则用于从外部获取一个模块的接口,即所获模块的exports对象。

下面以一个例子来了解模块。创建一个module.js文件,内容如下:

扫描二维码关注公众号,回复: 2719744 查看本文章
//module.js
var name;
exports.setName = function(thyName){
  name = thyName;
};

exports.sayHello = function(){
   console.log('Hello'+name);
};
在同一目录下创建getmodule.js内容如下:
// getmodule.js
var myModule = require('./module');
myModule.segName('BYVoid');
myModule.sayHello();

运行node getmodule.js , 结果是: Hello BYVoid

上例子中,module.js通过exports对象把setName和sayHello作为模块的访问接口,在getmodule.js中通过require('./module')加载这个模块,然后就可以直接访问modul.js中exports对象的成员函数。

这种接口封装方式比很多语言要简洁的多,同时不失优雅,未引入违反语义的特性,符合传统的编程逻辑,在这个基础上,我们可以构建大型的应用软件,npm提供上万个模块都是通过这种简单的方式搭建起来。

单次加载

无论调用多少次require,获得的模块都是同一个。

覆盖exports

有时候我们只是想把一个对象封装到模块中,例如:

// singleobject.js

function Hello(){
   var name;

   this.setName = function (thyName){

       name = thyName;

};

this.sayHello = function(){

    console.log('Hello ' + name);

};

};

exports.Hello = Hello;

在其他文件中需要通过require('./singleobject').Hello 来获取Hello对象,略显冗余,可以通过以下方法来简化。

//hello.js
function Hello() {
var name;
this.setName = function(thyName) {
name = thyName;
};
this.sayHello = function() {
console.log('Hello ' + name);
};
};
module.exports = Hello;
这样就可以直接获得这个对象了:
//gethello.js
var Hello = require('./hello');
hello = new Hello();
hello.setName('BYVoid');
hello.sayHello();
注意,模块接口的唯一变化是使用 module.exports = Hello 代替了 exports.Hello=
Hello。在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的
exports。
事实上,exports 本身仅仅是一个普通的空对象,即 {},它专门用来声明接口,本
质上是通过它为模块闭包①的内部建立了一个有限的访问接口。因为它没有任何特殊的地方,
所以可以用其他东西来代替,譬如我们上面例子中的 Hello 对象。

不可以通过对 exports 直接赋值代替对 module.exports 赋值。
exports 实际上只是一个和 module.exports 指向同一个对象的变量,
它本身会在模块执行结束后释放,但 module 不会,因此只能通过指定
module.exports 来改变访问接口。

猜你喜欢

转载自blog.csdn.net/Scrat_Kong/article/details/79148588