Node.js【5】核心模块

笔记来自《Node.js开发指南》BYVoid编著

第4章 Node.js核心模块


4.1、全局对象


Node.js中的全局对象是global,所有全局变量(除了global本身以外)都是global对象的属性。我们在Node.js中能够直接访问到对象通常都是global的属性,如console、process等。

永远使用var定义变量以避免引入全局变量,因为全局变量会污染命名空间,提高代码的耦合风险。

process用于描述当前Node.js进程状态的对象,提供了一个与操作系统的简单接口。

process.argv是命令行参数数组,第一个元素是node,第二个元素是脚本文件名,从第三个元素开始每个元素是一个运行参数。

process.stdout是标准输出流,通常我们使用的console.log()向标准输出打印字符,而process.stdout.write()函数提供了更底层的接口。

process.stdin是标准输入流,初始时它是被暂停的,要想从标准输入读取数据,你必须恢复流,并手动编写流的事件响应函数。

process.nextTick(callback)的功能是为事件循环设置一项任务,Node.js会在下次事件循环调响应时调用callback。
一个Node.js进程只有一个线程,因此在任何时刻都只有一个事件在执行。如果这个事件占用大量的CPU时间,执行事件循环中的下一个事件就需要等待很久,因此Node.js的一个编程原则就是尽量缩短每个事件的执行时间。process.nextTick()提供了一个这样的工具,可以把复杂的工作拆散,变成一个个较小的事件。供了一个这样的工具,可以把复杂的工作

process还展示了process.platform、process.pid、process.execPath、process.memoryUsage()等方法,以及POSIX进程信号响应机制。有兴趣的读者可以访问http://nodejs.org/api/process.html了解详细内容。

console用于向标准输出流(stdout)或标准错误流(stderr)输出字符。

console.log():向标准输出流打印字符并以换行符结束。如果有多个参数,则以类似于C语言printf()命令的格式输出。第一个参数是一个字符串,如果没有参数,只打印一个换行。

console.error():与console.log()用法相同,只是向标准错误流输出。

console.trace():向标准错误流输出当前的调用栈。

4.2、常用工具util


util是一个Node.js核心模块,提供常用函数的集合,用于弥补核心JavaScript的功能过于精简的不足。

util.inherits(constructor,superConstructor)是一个实现对象间原型继承的函数。

var util = require('util');

function Base() {
	this.name = 'base';
	this.base = 1989;

	this.sayHello = function() {
		console.log('Hello ' + this.name);
	};
}

Base.prototype.showName = function() {
	console.log(this.name);
};

function Sub() {
	this.name = 'sub';
}

util.inherits(Sub, Base);

var objBase = new Base();
objBase.showName();
objBase.sayHello();
console.log(objBase);

var objSub = new Sub();
objSub.showName();
//objSub.sayHello();
console.log(objSub);

/*
运行结果:
base
Hello base
{ name: 'base', base: 1991, sayHello: [Function] }
sub
{ name: 'sub' }
 */

Sub仅仅继承了Base在原型中定义的函数,而构造函数内部创造的base属性和sayHello函数都没有被Sub继承。同时,在原型中定义的属性不会被console.log作为对象的属性输出。

util.inspect(object,[showHidden],[depth],[colors])是一个将任意对象转换为字符串的方法,通常用于调试和错误输出。它至少接受一个参数object,即要转换的对象。
showHidden是一个可选参数,如果值为true,将会输出更多隐藏信息。
depth表示最大递归的层数,如果对象很复杂,你可以指定层数以控制输出信息的多少。如果不指定depth,默认会递归2层,指定为null表示将不限递归层数完整遍历对象。
如果color值为true,输出格式将会以ANSI颜色编码,通常用于在终端显示更漂亮的效果。

特别要指出的是,util.inspect并不会简单地直接把对象转换为字符串,即使该对象定义了toString方法也不会调用。

var util = require('util');

function Person() {
	this.name = 'ichenxiaodao';
	this.toString = function() {
		return this.name;
	};
}

var obj = new Person();

console.log(util.inspect(obj));
console.log(util.inspect(obj, true));

/*
运行结果:
{ name: 'ichenxiaodao', toString: [Function] }
{ name: 'ichenxiaodao',
  toString:
   { [Function]
     [length]: 0,
     [name]: '',
     [arguments]: null,
     [caller]: null,
     [prototype]: { [constructor]: [Circular] } } }
 */

除了以上我们介绍的几个函数之外,util还提供了util.isArray()、util.isRegExp()、util.isDate()、util.isError()四个类型测试工具,以及util.format()、util.debug()等工具。有兴趣的读者可以访问http://nodejs.org/api/util.html了解详细内容。


4.3、事件驱动events


events是Node.js最重要的模块,没有“之一”,原因是Node.js本身架构就是事件式的,而它提供了唯一的接口,所以堪称Node.js事件编程的基石。events模块不仅用于用户代码与Node.js下层事件循环的交互,还几乎被所有的模块依赖。

events模块只提供了一个对象:events.EventEmitter。EventEmitter的核心就是事件发射与事件监听器功能的封装。EventEmitter的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter支持若干个事件监听器。当事件发射时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。
var events = require('events');

var emitter = new events.EventEmitter();

//注册事件
//指定事件注册一个监听器,接受一个字符串event和一个回调函数listener。
emitter.on("someEvent", function(arg1, arg2) {
	console.log("listener1", arg1, arg2);
});

//注册事件
emitter.on("someEvent", function(arg1, arg2) {
	console.log("listener2", arg1, arg2);
});

//发射事件
//发射event事件,传递若干可选参数到事件监听器的参数表。
emitter.emit("someEvent", "ichenxiaodao", 1989);

/*
运行结果:
listener1 ichenxiaodao 1989
listener2 ichenxiaodao 1989
 */
EventEmitter.once(event,listener)为指定事件注册一个单次监听器,即监听器最多只会触发一次,触发后立刻解除该监听器。
EventEmitter.removeListener(event,listener)移除指定事件的某个监听器,listener必须是该事件已经注册过的监听器。
EventEmitter.removeAllListeners([event])移除所有事件的所有监听器,如果指定event,则移除指定事件的所有监听器。
更详细的API文档参见http://nodejs.org/api/events.html。

EventEmitter定义了一个特殊的事件error,它包含了“错误”的语义,我们在遇到异常的时候通常会发射error事件。当error被发射时,EventEmitter规定如果没有响应的监听器,Node.js会把它当作异常,退出程序并打印调用栈。我们一般要为会发射error事件的对象设置监听器,避免遇到错误后整个程序崩溃。
var events = require('events');

var emitter = new events.EventEmitter();

emitter.emit('error');

/*
运行结果:
events.js:74
        throw TypeError('Uncaught, unspecified "error" event.');
              ^
TypeError: Uncaught, unspecified "error" event.
    at TypeError (<anonymous>)
    at EventEmitter.emit (events.js:74:15)
    at Object.<anonymous> (/Users/cdz/workspace/my-node/NodeJS_Dev_Guide_Book/4/error.js:5:9)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3
 */
大多数时候我们不会直接使用EventEmitter,而是在对象中继承它。包括fs、net、http在内的,只要是支持事件响应的核心模块都是EventEmitter的子类。

原因是:首先,具有某个实体功能的对象实现事件符合语义,事件的监听和发射应该是一个对象的方法。其次JavaScript的对象机制是基于原型的,支持部分多重继承,继承EventEmitter不会打乱对象原有的继承关系。

4.4、文件系统fs


fs模块是文件操作的封装,它提供了文件的读取、写入、更名、删除、遍历目录、链接等POSIX文件系统操作。与其他模块不同的是,fs模块中所有的操作都提供了异步的和同步的两个版本,例如读取文件内容的函数有异步的fs.readFile()和同步的fs.readFileSync()。

fs.readFile(filename,[encoding],[callback(err,data)])是最简单的读取文件的函数。它接受一个必选参数filename,表示要读取的文件名。第二个参数encoding是可选的,表示文件的字符编码。callback是回调函数,用于接收文件的内容。如果不指定encoding,则callback就是第二个参数。回调函数提供两个参数err和data,err表示有没有错误发生,data是文件内容。如果指定了encoding,data是一个解析后的字符串,否则data将会是以Buffer形式表示的二进制数据。

Node.js的异步编程接口习惯是以函数的最后一个参数为回调函数,通常一个函数只有一个回调函数。回调函数是实际参数中第一个是err,其余的参数是其他返回的内容。如果没有发生错误,err的值会是null或undefined。如果有错误发生,err通常是Error对象的实例。

fs.readFileSync(filename,[encoding])是fs.readFile同步的版本。它接受的参数和fs.readFile相同,而读取到的文件内容会以函数返回值的形式返回。如果有错误发生,fs将会抛出异常,你需要使用try和catch捕捉并处理异常。

fs.open(path,flags,[mode],[callback(err,fd)])
fs.read(fd,buffer,offset,length,position,[callback(err,bytesRead,buffer)])

4.5、HTTP服务器与客户端


Node.js标准库提供了http模块,其中封装了一个高效的HTTP服务器和一个简易的HTTP客户端。http.Server是一个基于事件的HTTP服务器,它的核心由Node.js下层C++部分实现,而接口由JavaScript封装,兼顾了高性能与简易性。http.request则是一个HTTP客户端工具,用于向HTTP服务器发起请求。
var http = require('http');

http.createServer(function(req, res) {
	res.writeHead(200, {
		'Content-Type': 'text/html'
	});
	res.write('<h1>Node.js</h1>');
	res.end('<p>Hello World</p>');
}).listen(3000);

console.log("HTTP server is listening at port 3000.");
http.createServer创建了一个http.Server的实例,将一个函数作为HTTP请求处理函数。这个函数接受两个参数,分别是请求对象(req)和响应对象(res)。

http.Server是一个基于事件的HTTP服务器,所有的请求都被封装为独立的事件,开发者只需要对它的事件编写响应函数即可实现HTTP服务器的所有功能。它继承自EventEmitter

前面例子显式的实现方法:
var http = require('http');

var server = new http.Server();

server.on('request', function(req, res) {
	res.writeHead(200, {
		'Content-Type': 'text/html'
	});
	res.write('<h1>Node.js</h1>');
	res.end('<p>Hello World</p>');
});

server.listen(3000);

console.log("HTTP server is listening at port 3000.");
http.ServerRequest是HTTP请求的信息,是后端开发者最关注的内容。它一般由
http.Server的request事件发送,作为第一个参数传递,通常简称request或req。

由于GET请求直接被嵌入在路径中,URL是完整的请求路径,包括了?后面的部分,因此你可以手动解析后面的内容作为GET请求的参数。Node.js的url模块中的parse函数提供了这个功能。
var http = require('http');
var url = require('url');
var util = require('util');

http.createServer(function(req, res) {
	res.writeHead(200, {
		'Content-Type': 'text/plain'
	});
	res.end(util.inspect(url.parse(req.url, true)));
}).listen(3000);

/*
访问:
http://127.0.0.1:3000/user?name=ichenxiaodao&email=[email protected]
返回:
{ protocol: null,
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: null,
  search: '?name=ichenxiaodao&email=[email protected]',
  query: { name: 'ichenxiaodao', email: '[email protected]' },
  pathname: '/user',
  path: '/user?name=ichenxiaodao&email=[email protected]',
  href: '/user?name=ichenxiaodao&email=[email protected]' 
}

*/
手动解析POST请求体。
var http = require('http');
var querystring = require('querystring');
var util = require('util');

http.createServer(function(req, res) {
	var post = '';

	req.on('data', function(chunk) {
		post += chunk;
	});

	req.on('end', function(chunk) {
		post = querystring.parse(post);
		res.end(util.inspect(post));
	});
}).listen(3000);
http.ServerResponse是返回给客户端的信息,决定了用户最终能看到的结果。它也是由http.Server的request事件发送的,作为第二个参数传递,一般简称为response或res。

http.ServerResponse有三个重要的成员函数:
response.writeHead(statusCode,[headers])
response.write(data,[encoding])
response.end([data],[encoding])

http模块提供了两个函数http.request和http.get,功能是作为客户端向HTTP服务器发起请求。
var http = require('http');
var querystring = require('querystring');

var contents = querystring.stringify({
	name: "ichenxiaodao",
	email: "[email protected]",
	address: "Shanghai"
});

var options = {
	host: "www.byvoid.com",
	path: "/application/node/post.php",
	method: "POST",
	headers: {
		"Content-Type": "application/x-www-form-urlencoded",
		"Content-Length": contents.length
	}
};

var req = http.request(options, function(res) {
	res.setEncoding('utf-8');
	res.on('data', function(data) {
		console.log(data);
	});
});

req.write(contents);
req.end();
http.get(options,callback)http模块还提供了一个更加简便的方法用于处理GET请求:http.get。它是http.request的简化版,唯一的区别在于http.get自动将请求方法设为了GET请求,同时不需要手动调用req.end()。

http.ClientRequest是由http.request或http.get返回产生的对象,表示一个已经产生而且正在进行中的HTTP请求。它提供一个response事件,即http.request或http.get第二个参数指定的回调函数的绑定对象。

http.ClientRequest像http.ServerResponse一样也提供了write和end函数,用于向服务器发送请求体,通常用于POST、PUT等操作。所有写结束以后必须调用end函数以通知服务器,否则请求无效。

http.ClientResponse与http.ServerRequest相似,提供了三个事件data、end
和close,分别在数据到达、传输结束和连接结束时触发,其中data事件传递一个参数chunk,表示接收到的数据。


拓展阅读:http://nodejs.org/api/index.html



文档信息

  • 最后修改时间:2014年06月23日 01:04

Node.js【5】核心模块,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。