(四)NodeJs核心模块

本学习笔记是根据《Node.js开发指南》一书进行学习。

全局对象

JavaScript中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。在浏览器JavaScript中,通常window是全局对象,而NodeJs中的全局对象是global,所有全局变量(除了global本身以外)都是global对象的属性。

全局对象和全局变量

按照ECMAScript的定义,满足以下条件的变量是全局变量:

  • 在最外层定义的变量
  • 全局对象的属性
  • 隐式定义的变量(未定义直接赋值的变量)

在NodeJs中你不可能在最外层定义变量,因为所有用户代码都是属于当前模块的,而模块本身不是最外层上下文。

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

process

process是一个全局变量,即global对象的属性。它用于描述当前NodeJs进程状态的对象,提供了一个与操作系统的简单接口。

  • process.argv是命令行参数数组,第一个元素是node,第二个元素是脚本文件名,从第三个元素开始每个元素是一个运行参数。
  • process.stdout是标准输出流,通常我们使用的console.log()向标准输出打印字符,而process.stdout.write()函数提供了更底层的接口。
    -process.stdin是标准输入流,初始时它是被暂停的,要想从标准输入读取数据,你必须恢复流,并手动编写流的事件响应函数。
  • process.nextTick(callback)的功能是为事件循环设置一项任务,NodeJs会在下次事件循环调响应时调用callback
// debug.js
console.log(process.argv);

process.stdin.resume();
process.stdin.on("data", function (data) {
    process.stdout.write(data.toString());
});

结果:

image.png

不要使用setTimeout(fn, 0)代替process.nextTick(callback),前者比后者效率要低得多。

console

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

  • console.log()向标准输出流打印字符并以换行符结束。
  • console.error():与console.log()用法相同,只是向标准错误流输出。
  • console.trace():向标准错误流输出当前的调用栈。

console.log接受若干个参数,如果只有一个参数,则输出这个参数的字符串形式。如果有多个参数,则以类似于C语言printf()命令的格式输出。第一个参数是一个字符串,如果没有参数,只打印一个换行。

常用工具util

util是一个Node.js核心模块,提供常用函数的集合

util.inherits

JavaScript的面向对象特性是基于原型的,与常见的基于类的不同。JavaScript没有提供对象继承的语言级别特性,而是通过原型复制来实现的

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

// inherits.js
var util = require("util");

function Base() {
    this.name = "base";
    this.base = 1991;
    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 newBase = new Base();
newBase.showName();
newBase.sayHello();
console.log(newBase);

var newSub = new Sub();
newSub.showName();
// newSub.sayHello();
console.log(newSub);
image.png

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

util.inspect

util.inspect(object,[showHidden],[depth],[colors])是一个将任意对象转换为字符串的方法,通常用于调试和错误输出。它至少接受一个参数object,即要转换的对象。

util还提供了util.isArray()util.isRegExp()util.isDate()util.isError()四个类型测试工具,以及util.format()util.debug()等工具。

事件驱动events

events是NodeJs最重要的模块。NodeJs本身架构就是事件式的,而它提供了唯一的接口,所以堪称NodeJs事件编程的基石。

事件发射器

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

var EventEmitter = require("events").EventEmitter;
var event = new EventEmitter();
event.on("some_event", function(){
    console.log("some_event start1");
});
event.on("some_event", function () {
    console.log("some_event start2")
});

event.emit("some_event");

执行以上代码输出:

some_event start1
some_event start2

运行结果中可以看到两个事件监听器回调函数被先后调用。

EventEmitter常用的API:

  • EventEmitter.on(event, listener)为指定事件注册一个监听器,接受一个字符串event和一个回调函数listener
  • EventEmitter.emit(event, [arg1], [arg2], [...])发射event事件,传递若干可选参数到事件监听器的参数表。
  • EventEmitter.once(event, listener)为指定事件注册一个单次监听器,即监听器最多只会触发一次,触发后立刻解除该监听
  • EventEmitter.removeListener(event, listener)移除指定事件的某个监听器,listener必须是该事件已经注册过的监听器。
  • EventEmitter.removeAllListeners([event])移除所有事件的所有监听器,如果指定event,则移除指定事件的所有监听器。
var EventEmitter = require("events").EventEmitter;
var event = new EventEmitter();
event.on("some_event", function(){
    console.log("some_event start1");
});
event.on("some_event", function () {
    console.log("some_event start2")
});

event.emit("some_event");

setTimeout(function(){
    event.emit("some_event");
}, 1000);

event.removeAllListeners("some_event");
event.emit("some_event");

执行以上代码,输出:

image.png

error事件

EventEmitter定义了一个特殊的事件error,它包含了“错误”的语义,我们在遇到异常的时候通常会发射error事件。

继承EventEmitter

大多数时候我们不会直接使用EventEmitter,而是在对象中继承它。包括fsnethttp在内的,只要是支持事件响应的核心模块都是EventEmitter的子类。

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

文件系统fs

fs.readFile

fs.readFile(filename, [encoding], [callback(err, data)])是最简单的读取文件的函数。

var fs = require("fs");
fs.readFile("server.js", "utf-8", function(err, data){
    if (err){
        console.log(err);
    }else{
        console.log(data);
    }
})

fs.readFileSync

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

fs.open

fs.read

一般来说,除非必要,否则不要使用以上两种方式读取文件,因为它要求你手动管理缓冲区和文件指针,尤其是在你不知道文件大小的时候,这将会是一件很麻烦的事情。

HTTP服务器与客户端

HTTP服务器

http.Server的事件

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

  • request:当客户端请求到来时,该事件被触发,提供两个参数reqres,分别是http.ServerRequesthttp.ServerResponse的实例,表示请求和响应信息。
  • connection:当TCP连接建立时,该事件被触发,提供一个参数socket,为net.Socket的实例。connection事件的粒度要大于request,因为客户端在Keep-Alive模式下可能会在同一个连接内发送多次请求。
  • close :当服务器关闭时,该事件被触发。注意不是在用户连接断开时。
  • checkContinue、upgrade、clientError事件。

最常用的就是request了,因此http提供了一个捷径:http.createServer([requestListener]),功能是创建一个HTTP服务器并将requestListener作为request事件的监听函数。

http.ServerRequest

一般由http.Serverrequest事件发送,作为第一个参数传递,通常简称request或req。http.ServerRequest提供了以下3个事件用于控制请求体传输:

  • data :当请求体数据到来时,该事件被触发。该事件提供一个参数chunk,表示接收到的数据。如果该事件没有被监听,那么请求体将会被抛弃。该事件可能会被调用多次。
  • end :当请求体数据传输完成时,该事件被触发,此后将不会再有数据到来。
  • close:用户当前请求结束时,该事件被触发。不同于end,如果用户强制终止了传输,也还是调用close。

获取GET请求内容

url模块中的parse函数提供解析客户端的表单请求。

// httpServerRequestGet.js
var http = require("http");
var url = require("url");
var util = require("util");

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

在浏览器中访问http://127.0.0.1:3000/user?name=byvoid&email=byvoid@byvoid.com

image.jpg

通过url.parse,原始的path被解析为一个对象,其中query就是我们所谓的GET请求的内容,而路径则是pathname

获取POST请求内容

// httpServerRequestPost.js
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(){
        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调用之前,response.write可以被多次调用。
  • response.end([data], [encoding]): 结束响应,告知客户端所有发送已经完成。

HTTP客户端

http模块提供了两个函数http.requesthttp.get

  • http.request(options,callback)发起HTTP请求。接受两个参数,option是一个类似关联数组的对象,表示请求的参数,callback是请求的回调函数。
// httpRequst.js
var http = require("http");
var querystring = require("querystring");

var content = querystring.stringify({
    name: "byvoid",
    email: "byvoid@byvoid.com",
    address: "Tangshan",
});

var options = {
    host: "127.0.0.1",
    port: 3000,
    path: '/user?hello=wen',
    method: "POST",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        "Content-Length": content.length
    }
};

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

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

http.ClientRequest

http.ClientRequest是由http.requesthttp.get返回产生的对象,表示一个已经产生而且正在进行中的HTTP请求

http.ClientResponse

提供了三个事件dataendclose,分别在数据到达、传输结束和连接结束时触发,其中data事件传递一个参数chunk,表示接收到的数据。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • https://nodejs.org/api/documentation.html 工具模块 Assert 测试 ...
    KeKeMars阅读 6,305评论 0 6
  • 内容来自《Node.js开发指南》 核心模块是 Node.js 的心脏,它由一些精简而高效的库组成,为 Node....
    angelwgh阅读 888评论 0 1
  • Module definition patterns 除了作为加载依赖的机制之外,模块系统也是一种用于定义AP...
    宫若石阅读 457评论 0 0
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,600评论 18 139
  • 今天又惹晴宝宝生气了,原因在于自己本来说去找她,临时有事没有及时告诉她,让她多等了。事虽然不是特别严重的事,但是折...
    乱世小民阅读 125评论 2 0