Node Web学习笔记(4)-模块与包

前面介绍了Node的异步、事件驱动的重要特性,今天咱们来谈一谈模块与包的相关内容,了解模块与包的相关内容对组建一个项目是相当重要的。

1、文件模块的导出与引用

前面我们引用了httpfs,还有events,这些都是Node的核心模块,也就是自带模块。还有一类就是文件模块,用户可以自定义.js文件作为模块引入项目中。下面看一下自定义模块的例子。

1
2
3
4
5
//circle.js
var PI = Math.PI;
exports.area = function(r){
return PI*r*r;
}

上面是circle模块,显而易见,功能就是求一个圆的半径。

1
2
3
//app.js
var circle = require("./circle");
console.log("半径为5的圆的面积为:" + circle.area(5));

app.js引用circle模块,用require直接使用相对地址就可以引用一个文件模块了,这个没有什么需要需多说的。在circle文件模块中,有一个特殊的对象需要了解一下,那就是exports对象。circle.js文件模块输出了area()函数。

***要输出某个对象,把它加到exports这个特殊对象下即可。注意:exportsmodule.exports的一个引用,只是为了用起来方便。当你想输出的是例如构造函数这样的单个项目,那么需要使用module.exports 下面用代码来说明一下。

1
2
3
4
5
6
7
8
9
10
11
12
//写个Person类 - * penson.js *
function Person(){
var name ;
var age;
this.setName = function(theName){
name = theName;
}
this.getName = function(theAge){
console.log('My name is ' + name);
}
}
module.exports = Person;

代码主要就是实现了set和get方法,但是输出时并不是上面所说的加到exports特殊对象中进行输出,而是直接赋值给exports对象,那么这种情况的实现方式又是怎样呢?看下面代码。

1
2
3
4
5
//main.js
var Person = require("./Person");
var person = new Person(); //创建该模块的对象
person.setName("zhansan"); //对象调用函数
person.getName();

根据上面的例子,可以总结如下:

  • exports对象是当前模块的导出对象,用于导出模块公有方法和属性。别的模块通过require函数使用当前模块时得到的就是当前模块的exports对象。
  • module对象可以访问到当前模块的一些相关信息,但最多的用途是替换当前模块的导出对象。

2、模块的路径解析

模块的路径解析遵守以下几个规则:

  1. 核心模块定义在node源代码的lib/目录下,require()总是会优先加载核心模块。例如,require('http')总是返回编译好的HTTP模块,而不管是否有这个名字的文件模块;
  2. 如果按文件名没有查找到,那么node会添加.js.json后缀名,再尝试加载,如果还是没有找到,最后会加上.node的后缀名再次尝试加载;我上面就是直接写的文件名,而没有加后缀;
  3. .js 会被解析为Javascript纯文本文件,.json会被解析为JSON格式的纯文本文件, .node则会被解析为编译后的插件模块,由dlopen进行加载;
  4. 当没有以/./来指向一个文件时,这个模块要么是”核心模块”,要么就是从node_modules文件夹加载的;
  5. 如果指定的路径不存在,require()会抛出一个code属性为MODULE_NOT_FOUND的错误。
  6. 使用安装的模块(node_modules)的调用机制如下面的例子所示。如果位于/home/ry/projects/foo.js的文件调用了require('bar.js'),那么node查找的位置依次为:
    • /home/ry/projects/node_modules/bar.js
    • /home/ry/node_modules/bar.js
    • /home/node_modules/bar.js
    • /node_modules/bar.js

3、模块的初始化

一个模块中的JS代码仅在模块第一次被使用时执行一次,并在执行过程中初始化模块的导出对象。之后,缓存起来的导出对象被重复利用。

1
2
3
4
5
6
// load.js
var i = 0;
function count(){
return ++i;
}
exports.count = count;

该模块中定义了一个对象i,并输出公有方法count。主模块内容如下:

1
2
3
4
5
6
//main.js
var counter1 = require("./load");
var counter2 = require("./load");
console.log(counter1.count());
console.log(counter2.count());
console.log(counter2.count());

运行该例子node main.js,得到结果如下:

可以看到,load.js并没有因为require了两次而初始化两次。

模块和包的相关知识还有很多,这里只是简单介绍一下常用的一些知识。