一次完整的HTTP请求所经历的7个步骤

HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤:
1. 建立TCP连接
在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能进行更高层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80。
2. Web浏览器向Web服务器发送请求命令
一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令。例如:GET/sample/hello.jsp HTTP/1.1。
3. Web浏览器发送请求头信息
浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。
4. Web服务器应答
客户机向服务器发出请求后,服务器会客户机回送应答, HTTP/1.1 200 OK ,应答的第一部分是协议的版本号和应答状态码。
5. Web服务器发送应答头信息
正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。
6. Web服务器向浏览器发送数据
Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。
7. Web服务器关闭TCP连接
一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码:Connection:keep-alive
TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。


1、Node.js是运行在服务器端的JavaScript,是一个基于Chrome JavaScript运行时建立的一个平台。

2、nodejs的两种模式

1)脚本模式:在js文件中写代码,然后输入文件路径, node 文件路径 回车就会输出相应的结果
2)交互模式:打开终端,键入node进入命令交互模式,可以输入一条代码语句后立即执行并显示结果
按两次 ctrl+c可以退出交互模式

3、Node.js创建第一个应用

使用Node.js时,我们不仅仅在实现一个应用,同时还实现了整个HTTP服务器。事实上,我们的web应用
以及对应的web服务器基本上是一样的。

了解一下Node.js应用是由哪几部分组成的:

1、引入required 模块:我们可以使用require指令来载入Node.js模块
2、创建服务器:服务器可以监听客户端的请求,类似于Apache、Nginx等HTTP服务器。
3、接收请求与响应请求:服务器很容易创建,客户端可以使用浏览器或终端发送HTTP请求,
服务器接收请求后返回响应数据。

4、创建Node.js应用

步骤一、引入required模块
我们使用require指令来载入http模块,并将实例化的HTTP赋值给变量http,实例如下:
var http= require("http");

步骤二、创建服务器
使用http.createServer()方法创建服务器,并使用listen方法绑定8888端口。函数通过
request,response参数来接收和响应数据。

实例如下:在你项目的根目录下创建一个叫server.js的文件,并写入以下代码:

var http=require("http");
http.createServer(function(request,response){
//发送HTTP 头部
//HTTP状态值: 200:ok
//内容类型 :text/plain
response.writeHead(200,{'Content-Type':'text/plain'});

//发送响应数据“Hello World”
response.end('Hello World\n');

}).listen(8888);

//终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');

以上代码我们完成了一个可以工作的HTTP服务器。

使用node命令执行以上的代码:
node server.js
Server running at http://127.0.0.1:8888/


分析Node.js的HTTP服务器:

1)第一行请求(require)Node.js自带的http模块,并且把它赋值给http变量
2)接下来,我们调用http模块提供的函数:createServer。这个函数会返回一个对象,这个对象
有一个叫做listen的方法,这个方法有一个参数,指定这个HTTP服务器监听的端口号。

5、NPM使用介绍

NPM是随同NodeJS一起安装的包管理工具,能解决NodeJs代码部署上的很多问题,常见的使用场景
有以下几种:

1)允许用户从NPM服务器下载别人编写的第三方包到本地使用
2)允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用
3)允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用

npm -v 查看npm版本
npm install npm -g npm版本升级

6、使用npm命令安装模块

语法:npm install <模块名>

例如:使用npm命令安装常用的Node.js web框架模块express
npm install express
安装好之后,express包就放在了工程目录下的node_modules目录中,因此在代码中只需要通过
require('express')的方式就好,无需指定第三方包路径

var express=require('express');

7、全局安装与本地安装

npm install express //本地安装
nom install express -g //全局安装

如果出现以下错误:

npm err! Error:connect ECONNREFUSED 127.0.0.1:8087

解决办法为:

npm config set proxy null



本地安装:
1、将安装包放在./node_moudules下(运行npm命令时所在的目录),如果没有node_modules
目录,当前执行npm命令的目录下生成node_module目录。
2、可以通过require()来引入本地安装的包

全局安装
1、将安装包放在/usr/local下或者你node的安装目录
2、可以直接在命令行里使用


使用全局方式安装 express
npm install express -g

使用package.json
package.json位于模块的目录下,用于定义包的属性。

8、卸载模块
我们可以使用以下命令来卸载Node.js模块
npm uninstall express

更新模块:
npm update express

搜索模块

npm search express

创建模块

创建模块,package.json 文件是必不可少的。我们可以使用 NPM 生成 package.json 文件,生成的文件包含了基本的结果。
$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (node_modules) runoob # 模块名
version: (1.0.0)
description: Node.js 测试模块(www.runoob.com) # 描述
entry point: (index.js)
test command: make test
git repository: https://github.com/runoob/runoob.git # Github 地址
keywords:
author:
license: (ISC)
About to write to ……/node_modules/package.json: # 生成地址

{
"name": "runoob",
"version": "1.0.0",
"description": "Node.js 测试模块(www.runoob.com)",
……
}


Is this ok? (yes) yes
以上的信息,你需要根据你自己的情况输入。在最后输入 "yes" 后会生成 package.json 文件。
接下来我们可以使用以下命令在 npm 资源库中注册用户(使用邮箱注册):
$ npm adduser
Username: mcmohd
Password:
Email: (this IS public) mcmohd@gmail.com
接下来我们就用以下命令来发布模块:
$ npm publish
如果你以上的步骤都操作正确,你就可以跟其他模块一样使用 npm 来安装

9、npm版本号

使用npm下载和发布代码时都会接触到版本号,在申请第三方包依赖时,除了可依赖于一个固定版本号外,还可依赖于某一
范围的版本号。

10、npm常用命令

NPM提供了很多命令,例如install和publish,使用npm help可查看所有命令

1)PM提供了很多命令,例如install和publish,使用npm help可查看所有命令
2)使用npm help <command>可查看某条命令的详细帮助,例如 npm help install
3)在package.json所在目录下使用npm install . -g可先在本地安装当前命令行程序,可用于发布前的本地测试。
4)使用npm update <package>可以把当前目录下node_modules子目录里边的对应模块更新至最新版本。
5)使用npm update <package> -g可以把全局安装的对应命令行程序更新至最新版。
6)使用npm cache clear可以清空NPM本地缓存,用于对付使用相同版本号发布新版本代码的人。
7)使用npm unpublish <package>@<version>可以撤销发布自己发布过的某个版本代码。

11、Node.js REPL(交互式解释器)

Node.jsREPL表示一个电脑环境,类似Window系统的终端或unix/Linux shell,我们可以在终端输入命令,并接收系统的响应
Node 自带了交互式解释器,可以执行以下任务:
1)读取 - 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。
2)执行 - 执行输入的数据结构
3)打印 - 输出结果
4)循环 - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。

Node 的交互式解释器可以很好的调试 Javascript 代码。
开始学习 REPL
我们可以输入以下命令来启动 Node 的终端:

$ node
>
这时我们就可以在 > 后输入简单的表达式,并按下回车键来计算结果。
简单的表达式运算
接下来让我们在 Node.js REPL 的命令行窗口中执行简单的数学运算:
$ node
> 1 +4
5
> 5 / 2
2.5
> 3 * 6
18
> 4 - 1
3
> 1 + ( 2 * 3 ) - 4
3
>

使用变量
你可以将数据存储在变量中,并在你需要的时候使用它。
变量声明需要使用 var 关键字,如果没有使用 var 关键字变量会直接打印出来。
使用 var 关键字的变量可以使用 console.log() 来输出变量。

$ node
> x = 10
10
> var y = 10
undefined
> x + y
20
> console.log("Hello World")
Hello World
undefined
> console.log("www.runoob.com")
www.runoob.com
undefined

多行表达式
Node REPL 支持输入多行表达式,这就有点类似 JavaScript。接下来让我们来执行一个 do-while 循环:

$ node
> var x = 0
undefined
> do {
... x++;
... console.log("x: " + x);
... } while ( x < 5 );
x: 1
x: 2
x: 3
x: 4
x: 5
undefined
>
... 三个点的符号是系统自动生成的,你回车换行后即可。Node 会自动检测是否为连续的表达式。


下划线(_)变量
你可以使用下划线(_)获取表达式的运算结果:

$ node
> var x = 10
undefined
> var y = 20
undefined
> x + y
30
> var sum = _
undefined
> console.log(sum)
30
undefined
>

REPL 命令:

ctrl + c - 退出当前终端。
ctrl + c 按下两次 - 退出 Node REPL。
ctrl + d - 退出 Node REPL.
向上/向下 键 - 查看输入的历史命令
tab 键 - 列出当前命令
.help - 列出使用命令
.break - 退出多行表达式
.clear - 退出多行表达式
.save filename - 保存当前的 Node REPL 会话到指定文件
.load filename - 载入当前 Node REPL 会话的文件内容。


12、Node.js的回调函数

Node.js异步编程的直接体现就是回调。
异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。
回调函数在完成任务后就会被调用,Node使用了大量的回调函数,Node所有API都
支持回调函数。
例如:我们可以一边读取文件,一边执行其他命令,在文件读取完成之后,我们将文件内容作为回调函数的参数返回。这样
在执行代码时就没有阻塞或等待文件I/O操作。这就大大提高了Node.js的性能,可以处理大量的并发请求。


阻塞代码实例
创建一个文件 input.txt ,内容如下:
菜鸟教程官网地址:www.runoob.com
创建 main.js 文件, 代码如下:
var fs = require("fs");

var data = fs.readFileSync('input.txt');

console.log(data.toString());
console.log("程序执行结束!");
以上代码执行结果如下:
$ node main.js
菜鸟教程官网地址:www.runoob.com

程序执行结束!
非阻塞代码实例
创建一个文件 input.txt ,内容如下:
菜鸟教程官网地址:www.runoob.com
创建 main.js 文件, 代码如下:
var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});

console.log("程序执行结束!");
以上代码执行结果如下:
$ node main.js
程序执行结束!
菜鸟教程官网地址:www.runoob.com

以上两个实例我们了解了阻塞与非阻塞调用的不同。第一个实例在文件读取完后才执行完程序。 第二个实例我们不需要等待文件读取完,
这样就可以在读取文件时同时执行接下来的代码,大大提高了程序的性能。
因此,阻塞是按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,我们就需要写在回调函数内。


13、Node.js事件循环

Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高。
Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发。
Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。
Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.

 

事件驱动程序
Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。
当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。
这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO)
在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。

整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。
Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,
如下实例:

// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();
以下程序绑定事件处理程序:
// 绑定事件及事件的处理程序
eventEmitter.on('eventName', eventHandler);

我们可以通过程序触发事件:
// 触发事件
eventEmitter.emit('eventName');

 

实例

创建main.js文件,代码如下所示:

//引入events模块
var events=require('events');
//创建eventEmitter对象
var eventEmitter=new events.EventEmitter();
//创建事件处理程序
var connectHandler=function connected(){
console.log('连接成功');
//在事件处理程序中触发data_received事件
eventEmitter.emit('data_received');
};
//绑定connection事件处理程序
eventEmitter.on('connection',connectHandler);
//使用匿名函数绑定data_received事件
eventEmitter.on('data_received',function(){
console.log('数据连接成功。');
});
//触发connection事件
eventEmitter.emit('connection');
console.log("程序执行完毕");

接下来让我们执行以上代码:
$ node main.js
连接成功。
数据接收成功。
程序执行完毕。

Node 应用程序是如何工作的?
在 Node 应用程序中,执行异步操作的函数将回调函数(如以下示例中的
fs.readFile())作为最后一个参数, 回调函数接收错误对象作为第一个参数。
接下来让我们来重新看下前面的实例,创建一个 input.txt ,文件内容如下:
菜鸟教程官网地址:www.runoob.com
创建 main.js 文件,代码如下:
var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
if (err){
console.log(err.stack);
return;
}
console.log(data.toString());
});
console.log("程序执行完毕");
以上程序中 fs.readFile() 是异步函数用于读取文件。 如果在读取文件过程中发生错误,错误 err 对象就会输出错误信息。
如果没发生错误,readFile 跳过 err 对象的输出,文件内容就通过回调函数输出。
执行以上代码,执行结果如下:
程序执行完毕
菜鸟教程官网地址:www.runoob.com
接下来我们删除 input.txt 文件,执行结果如下所示:
程序执行完毕
Error: ENOENT, open 'input.txt'
因为文件 input.txt 不存在,所以输出了错误信息。

Node.js EventEmitter
Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。
Node.js里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个
事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件。 所有这些产生事
件的对象都是 events.EventEmitter 的实例。


EventEmitter 类
events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。
你可以通过require("events");来访问该模块。
// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();


EventEmitter 对象如果在实例化时发生错误,会触发 'error' 事件。当添加新的监听器时,'newListener' 事件会触发,当监听器被移除时,'removeListener' 事件被触发。
下面我们用一个简单的例子说明 EventEmitter 的用法:
//event.js 文件
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();
event.on('some_event', function() {
console.log('some_event 事件触发');
});
setTimeout(function() {
event.emit('some_event');
}, 1000);
执行结果如下:
运行这段代码,1 秒后控制台输出了 'some_event 事件触发'。其原理是 event 对象注册了事件 some_event 的一个监听器,然后我们通过 setTimeout 在 1000 毫秒以后向 event 对象发送事件 some_event,此时会调用some_event 的监听器。
$ node event.js
some_event 事件触发
EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持 若干个事件监听器。
当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。
让我们以下面的例子解释这个过程:
//event.js 文件
var events = require('events');
var emitter = new events.EventEmitter();
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener1', arg1, arg2);
});
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener2', arg1, arg2);
});
emitter.emit('someEvent', 'arg1 参数', 'arg2 参数');
执行以上代码,运行的结果如下:
$ node event.js
listener1 arg1 参数 arg2 参数
listener2 arg1 参数 arg2 参数

以上例子中,emitter 为事件 someEvent 注册了两个事件监听器,然后触发了 someEvent 事件。
运行结果中可以看到两个事件监听器回调函数被先后调用。 这就是EventEmitter最简单的用法。
EventEmitter 提供了多个属性,如 on 和 emit。on 函数用于绑定事件函数,emit 属性用于触发
一个事件。接下来我们来具体看下 EventEmitter 的属性介绍。

方法
序号 方法 & 描述
1 addListener(event, listener)
为指定事件添加一个监听器到监听器数组的尾部。
2 on(event, listener)
为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。
server.on('connection', function (stream) {
console.log('someone connected!');
});
3 once(event, listener)
为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
server.once('connection', function (stream) {
console.log('Ah, we have our first user!');
});

4 removeListener(event, listener)
移除指定事件的某个监听器,监听器 必须是该事件已经注册过的监听器。
var callback = function(stream) {
console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
5 removeAllListeners([event])
移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。
6 setMaxListeners(n)
默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。
7 listeners(event)
返回指定事件的监听器数组。
8 emit(event, [arg1], [arg2], [...])
按参数的顺序执行每个监听器,如果事件有注册监听返回 true,否则返回 false。


类方法
序号 方法 & 描述
1 listenerCount(emitter, event)
返回指定事件的监听器数量。


事件
序号 事件 & 描述
1 newListener
event - 字符串,事件名称
listener - 处理事件函数
该事件在添加新监听器时被触发。
2 removeListener
event - 字符串,事件名称
listener - 处理事件函数
从指定监听器数组中删除一个监听器。需要注意的是,此操作将会改变处于被删监
听器之后的那些监听器的索引。


实例:

/**
* Created by xxw on 2017.03.24.
*/
var events=require('events');
var eventEmitter=new events.EventEmitter();

//监听器1
var listener1=function listener1(){
console.log('监听器listener1 执行。');
};
//监听器2
var listener2=function listener2(){
console.log('监听器listener2 执行。');
};

//绑定connection 事件 ,处理函数为listener1

eventEmitter.addListener('connection',listener1);
//绑定connection 事件 ,处理函数为listener2

eventEmitter.on('connection',listener2);

var eventListeners=events.EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners+"个监听器监听连接事件。");

//触发connection 事件

eventEmitter.emit('connection');

//移除绑定的listener1函数
eventEmitter.removeListener('connection',listener1);
console.log("listener1 不再受监听");

//触发连接事件

eventEmitter.emit('connection');

eventListeners=events.EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners+"个监听连接事件。");
console.log("程序执行完毕。");


以上代码,执行结果如下所示:
$ node main.js
2 个监听器监听连接事件。
监听器 listener1 执行。
监听器 listener2 执行。
listener1 不再受监听。
监听器 listener2 执行。
1 个监听器监听连接事件。
程序执行完毕。


error事件


EventEmitter 定义了一个特殊的事件 error,它包含了错误的语义,我们在遇到 异常的时候通常会触发 error 事件。
当 error 被触发时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。
我们一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。例如:

 

继承EventEmitter

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

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

14、Node.js Buffer(缓冲区)

JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。
但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。

在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理I/O操作中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。
一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。


创建 Buffer 类
Node Buffer 类可以通过多种方式来创建。
方法 1
创建长度为 10 字节的 Buffer 实例:
var buf = new Buffer(10);
方法 2
通过给定的数组创建 Buffer 实例:
var buf = new Buffer([10, 20, 30, 40, 50]);
方法 3
通过一个字符串来创建 Buffer 实例:
var buf = new Buffer("www.runoob.com", "utf-8");

utf-8 是默认的编码方式,此外它同样支持以下编码:"ascii", "utf8", "utf16le", "ucs2", "base64" 和 "hex"。


写入缓冲区
语法
写入 Node 缓冲区的语法如下所示:
buf.write(string[, offset[, length]][, encoding])
参数
参数描述如下:
string - 写入缓冲区的字符串。
offset - 缓冲区开始写入的索引值,默认为 0 。
length - 写入的字节数,默认为 buffer.length
encoding - 使用的编码。默认为 'utf8' 。
返回值
返回实际写入的大小。如果 buffer 空间不足, 则只会写入部分字符串。

实例:

var buf=new Buffer(256);
var len=buf.write("www.baidu.com");
console.log("写入字节数:"+len);


执行以上代码,输出结果为:

$node main.js
写入字节数 : 13

从缓冲区读取数据
语法
读取 Node 缓冲区数据的语法如下所示:
buf.toString([encoding[, start[, end]]])
参数
参数描述如下:
encoding - 使用的编码。默认为 'utf8' 。
start - 指定开始读取的索引位置,默认为 0。
end - 结束位置,默认为缓冲区的末尾。
返回值
解码缓冲区数据并使用指定的编码返回字符串。


实例
buf = new Buffer(26);
for (var i = 0 ; i < 26 ; i++) {
buf[i] = i + 97;
}

console.log( buf.toString('ascii')); // 输出: abcdefghijklmnopqrstuvwxyz
console.log( buf.toString('ascii',0,5)); // 输出: abcde
console.log( buf.toString('utf8',0,5)); // 输出: abcde
console.log( buf.toString(undefined,0,5)); // 使用 'utf8' 编码, 并输出: abcde
执行以上代码,输出结果为:
$ node main.js
abcdefghijklmnopqrstuvwxyz
abcde
abcde
abcde

 

将 Buffer 转换为 JSON 对象
语法
将 Node Buffer 转换为 JSON 对象的函数语法格式如下:
buf.toJSON()
返回值
返回 JSON 对象。
实例
var buf = new Buffer('www.runoob.com');
var json = buf.toJSON(buf);

console.log(json);

执行以上代码,输出结果为:
{ type: 'Buffer',
data: [ 119, 119, 119, 46, 98, 97, 105, 100, 117, 46, 99, 111, 109 ]
}

缓冲区合并
语法
Node 缓冲区合并的语法如下所示:
Buffer.concat(list[, totalLength])
参数
参数描述如下:
list - 用于合并的 Buffer 对象数组列表。
totalLength - 指定合并后Buffer对象的总长度。
返回值
返回一个多个成员合并的新 Buffer 对象。
实例:

var buffer1 = new Buffer('菜鸟教程 ');
var buffer2 = new Buffer('www.runoob.com');
var buffer3 = Buffer.concat([buffer1,buffer2]);
console.log("buffer3 内容: " + buffer3.toString());
执行以上代码,输出结果为:
buffer3 内容: 菜鸟教程 www.runoob.com


缓冲区比较
语法
Node Buffer 比较的函数语法如下所示, 该方法在 Node.js v0.12.2 版本引入:
buf.compare(otherBuffer);
参数
参数描述如下:
otherBuffer - 与 buf 对象比较的另外一个 Buffer 对象。
返回值
返回一个数字,表示 buf 在 otherBuffer 之前,之后或相同。

实例:

ar buffer1=new Buffer("ashgkghlfd");
var buffer2=new Buffer("aAJDGHJKLUd");
var result=buffer1.compare(buffer2);
if(result<0){
console.log(buffer1+"在"+buffer2 +"之前");
}else if(result==0){
console.log(buffer1+"与"+buffer2+"相同");
}else{
console.log(buffer1+"在"+buffer2+"之后");
}


执行以上代码,输出结果为:

ashgkghlfd在aAJDGHJKLUd之后

拷贝缓冲区
语法
Node 缓冲区拷贝语法如下所示:
buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
参数
参数描述如下:
targetBuffer - 要拷贝的 Buffer 对象。
targetStart - 数字, 可选, 默认: 0
sourceStart - 数字, 可选, 默认: 0
sourceEnd - 数字, 可选, 默认: buffer.length
返回值
没有返回值。

实例:

var buffer2 = new Buffer(3);
buffer1.copy(buffer2);
console.log("buffer2 content: "+buffer2.toString());


执行以上代码,输出结果为:
buffer2 content: ABC


缓冲区裁剪
Node 缓冲区裁剪语法如下所示:
buf.slice([start[, end]])
参数
参数描述如下:
start - 数字, 可选, 默认: 0
end - 数字, 可选, 默认: buffer.length
返回值
返回一个新的缓冲区,它和旧缓冲区指向同一块内存,但是从索引 start 到 end 的位置剪切。
实例:

var buffer1=new Buffer("baidu");
var buffer2=buffer1.slice(0,4);
console.log("buffer2 content: "+buffer2.toString());

执行以上代码,输出结果为:

buffer2 content: baid

缓冲区长度

语法
Node 缓冲区长度计算语法如下所示:
buf.length;
返回值
返回 Buffer 对象所占据的内存长度。
实例
var buffer = new Buffer('www.runoob.com');
// 缓冲区长度
console.log("buffer length: " + buffer.length);
执行以上代码,输出结果为:
buffer length: 14


方法参考手册
以下列出了 Node.js Buffer 模块常用的方法(注意有些方法在旧版本是没有的):
序号 方法 & 描述
1 new Buffer(size)
分配一个新的 size 大小单位为8位字节的 buffer。 注意, size 必须小于 kMaxLength,否则,将会抛出异常 RangeError。
2 new Buffer(buffer)
拷贝参数 buffer 的数据到 Buffer 实例。
3 new Buffer(str[, encoding])
分配一个新的 buffer ,其中包含着传入的 str 字符串。 encoding 编码方式默认为 'utf8'。
4 buf.length
返回这个 buffer 的 bytes 数。注意这未必是 buffer 里面内容的大小。length 是 buffer 对象所分配的内存数,它不会随着这个 buffer 对象内容的改变而改变。
5 buf.write(string[, offset[, length]][, encoding])
根据参数 offset 偏移量和指定的 encoding 编码方式,将参数 string 数据写入buffer。 offset 偏移量默认值是 0, encoding 编码方式默认是 utf8。 length 长度是将要写入的字符串的 bytes 大小。 返回 number 类型,表示写入了多少 8 位字节流。如果 buffer 没有足够的空间来放整个 string,它将只会只写入部分字符串。 length 默认是 buffer.length - offset。 这个方法不会出现写入部分字符。
6 buf.writeUIntLE(value, offset, byteLength[, noAssert])
将value 写入到 buffer 里, 它由offset 和 byteLength 决定,支持 48 位计算,例如:
var b = new Buffer(6);
b.writeUIntBE(0x1234567890ab, 0, 6);
// <Buffer 12 34 56 78 90 ab> noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。
7 buf.writeUIntBE(value, offset, byteLength[, noAssert])
将value 写入到 buffer 里, 它由offset 和 byteLength 决定,支持 48 位计算。noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。
8 buf.writeIntLE(value, offset, byteLength[, noAssert])
将value 写入到 buffer 里, 它由offset 和 byteLength 决定,支持 48 位计算。noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。
9 buf.writeIntBE(value, offset, byteLength[, noAssert])
将value 写入到 buffer 里, 它由offset 和 byteLength 决定,支持 48 位计算。noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。
10 buf.readUIntLE(offset, byteLength[, noAssert])
支持读取 48 位以下的数字。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。
11 buf.readUIntBE(offset, byteLength[, noAssert])
支持读取 48 位以下的数字。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。
12 buf.readIntLE(offset, byteLength[, noAssert])
支持读取 48 位以下的数字。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。
13 buf.readIntBE(offset, byteLength[, noAssert])
支持读取 48 位以下的数字。noAssert 值为 true 时, offset 不再验证是否超过 buffer 的长度,默认为 false。
14 buf.toString([encoding[, start[, end]]])
根据 encoding 参数(默认是 'utf8')返回一个解码过的 string 类型。还会根据传入的参数 start (默认是 0) 和 end (默认是 buffer.length)作为取值范围。
15 buf.toJSON()
将 Buffer 实例转换为 JSON 对象。
16 buf[index]
获取或设置指定的字节。返回值代表一个字节,所以返回值的合法范围是十六进制0x00到0xFF 或者十进制0至 255。
17 buf.equals(otherBuffer)
比较两个缓冲区是否相等,如果是返回 true,否则返回 false。
18 buf.compare(otherBuffer)
比较两个 Buffer 对象,返回一个数字,表示 buf 在 otherBuffer 之前,之后或相同。
19 buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
buffer 拷贝,源和目标可以相同。 targetStart 目标开始偏移和 sourceStart 源开始偏移默认都是 0。 sourceEnd 源结束位置偏移默认是源的长度 buffer.length 。
20 buf.slice([start[, end]])
剪切 Buffer 对象,根据 start(默认是 0 ) 和 end (默认是 buffer.length ) 偏移和裁剪了索引。 负的索引是从 buffer 尾部开始计算的。
21 buf.readUInt8(offset[, noAssert])
根据指定的偏移量,读取一个有符号 8 位整数。若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 如果这样 offset 可能会超出buffer 的末尾。默认是 false。
22 buf.readUInt16LE(offset[, noAssert])
根据指定的偏移量,使用特殊的 endian 字节序格式读取一个有符号 16 位整数。若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。
23 buf.readUInt16BE(offset[, noAssert])
根据指定的偏移量,使用特殊的 endian 字节序格式读取一个有符号 16 位整数。若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。
24 buf.readUInt32LE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian 字节序格式读取一个有符号 32 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。
25 buf.readUInt32BE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian 字节序格式读取一个有符号 32 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。
26 buf.readInt8(offset[, noAssert])
根据指定的偏移量,读取一个 signed 8 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。
27 buf.readInt16LE(offset[, noAssert])
根据指定的偏移量,使用特殊的 endian 格式读取一个 signed 16 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。
28 buf.readInt16BE(offset[, noAssert])
根据指定的偏移量,使用特殊的 endian 格式读取一个 signed 16 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出 buffer 的末尾。默认是 false。
29 buf.readInt32LE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian 字节序格式读取一个 signed 32 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。
30 buf.readInt32BE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian 字节序格式读取一个 signed 32 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。
31 buf.readFloatLE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian 字节序格式读取一个 32 位浮点数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer的末尾。默认是 false。
32 buf.readFloatBE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian 字节序格式读取一个 32 位浮点数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer的末尾。默认是 false。
33 buf.readDoubleLE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian字节序格式读取一个 64 位double。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。
34 buf.readDoubleBE(offset[, noAssert])
根据指定的偏移量,使用指定的 endian字节序格式读取一个 64 位double。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 offset 可能会超出buffer 的末尾。默认是 false。
35 buf.writeUInt8(value, offset[, noAssert])
根据传入的 offset 偏移量将 value 写入 buffer。注意:value 必须是一个合法的有符号 8 位整数。 若参数 noAssert 为 true 将不会验证 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则不要使用。默认是 false。
36 buf.writeUInt16LE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的有符号 16 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出buffer的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
37 buf.writeUInt16BE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的有符号 16 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出buffer的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
38 buf.writeUInt32LE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式(LITTLE-ENDIAN:小字节序)将 value 写入buffer。注意:value 必须是一个合法的有符号 32 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着value 可能过大,或者offset可能会超出buffer的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
39 buf.writeUInt32BE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式(Big-Endian:大字节序)将 value 写入buffer。注意:value 必须是一个合法的有符号 32 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者offset可能会超出buffer的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
40 buf.writeInt8(value, offset[, noAssert])
41 buf.writeInt16LE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 16 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false 。
42 buf.writeInt16BE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 16 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false 。
43 buf.writeInt32LE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 32 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
44 buf.writeInt32BE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个合法的 signed 32 位整数。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
45 buf.writeFloatLE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer 。注意:当 value 不是一个 32 位浮点数类型的值时,结果将是不确定的。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
46 buf.writeFloatBE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer 。注意:当 value 不是一个 32 位浮点数类型的值时,结果将是不确定的。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value可能过大,或者 offset 可能会超出 buffer 的末尾从而造成 value 被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
47 buf.writeDoubleLE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个有效的 64 位double 类型的值。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成value被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
48 buf.writeDoubleBE(value, offset[, noAssert])
根据传入的 offset 偏移量和指定的 endian 格式将 value 写入 buffer。注意:value 必须是一个有效的 64 位double 类型的值。 若参数 noAssert 为 true 将不会验证 value 和 offset 偏移量参数。 这意味着 value 可能过大,或者 offset 可能会超出 buffer 的末尾从而造成value被丢弃。 除非你对这个参数非常有把握,否则尽量不要使用。默认是 false。
49 buf.fill(value[, offset][, end])
使用指定的 value 来填充这个 buffer。如果没有指定 offset (默认是 0) 并且 end (默认是 buffer.length) ,将会填充整个buffer。

15、Node.js Stream(流)

Stream 是一个抽象接口,

Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。
Node.js,Stream 有四种流类型:

Readable - 可读操作。
Writable - 可写操作。
Duplex - 可读可写操作.
Transform - 操作被写入数据,然后读出结果。
所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:
data - 当有数据可读时触发。
end - 没有更多的数据可读时触发。
error - 在接收和写入过程中发生错误时触发。
finish - 所有数据已被写入到底层系统时触发。


从流中读取数据
创建 input.txt 文件(注意保存时设置编码格式),内容如下:
菜鸟教程官网地址:www.runoob.com
创建 main.js 文件, 代码如下:
var fs = require("fs");
var data = '';

// 创建可读流
var readerStream = fs.createReadStream('input.txt');

// 设置编码为 utf8。
readerStream.setEncoding('UTF8');

// 处理流事件 --> data, end, and error
readerStream.on('data', function(chunk) {
data += chunk;
});

readerStream.on('end',function(){
console.log(data);
});

readerStream.on('error', function(err){
console.log(err.stack);
});

console.log("程序执行完毕");
以上代码执行结果如下:
程序执行完毕
菜鸟教程官网地址:www.runoob.com


写入流
创建 main.js 文件, 代码如下:
var fs = require("fs");
var data = '菜鸟教程官网地址:www.runoob.com';

// 创建一个可以写入的流,写入到文件 output.txt 中
var writerStream = fs.createWriteStream('output.txt');

// 使用 utf8 编码写入数据
writerStream.write(data,'UTF8');

// 标记文件末尾
writerStream.end();

// 处理流事件 --> data, end, and error
writerStream.on('finish', function() {
console.log("写入完成。");
});

writerStream.on('error', function(err){
console.log(err.stack);
});

console.log("程序执行完毕");
以上程序会将 data 变量的数据写入到 output.txt 文件中。代码执行结果如下:
$ node main.js
程序执行完毕
写入完成。
查看 output.txt 文件的内容:
$ cat output.txt
菜鸟教程官网地址:www.runoob.com


管道流
管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递
到另外一个流中。

如上面的图片所示,我们把文件比作装水的桶,而水就是文件里的内容,我们用一根管子(pipe)连接两个桶使得水从一个桶流入另一个桶,这样就慢慢的实现了大文件的复制过程。
以下实例我们通过读取一个文件内容并将内容写入到另外一个文件中。
设置 input.txt 文件内容如下:

菜鸟教程官网地址:www.runoob.com
管道流操作实例
创建 main.js 文件, 代码如下:
var fs = require("fs");

// 创建一个可读流
var readerStream = fs.createReadStream('input.txt');

// 创建一个可写流
var writerStream = fs.createWriteStream('output.txt');

// 管道读写操作
// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
readerStream.pipe(writerStream);

console.log("程序执行完毕");


代码执行结果如下:
$ node main.js
程序执行完毕

查看 output.txt 文件的内容:
$ cat output.txt
菜鸟教程官网地址:www.runoob.com
管道流操作实例


链式流
链式是通过连接输出流到另外一个流并创建多个对个流操作链的机制。链式流一般用于管道操作。
接下来我们就是用管道和链式来压缩和解压文件。
创建 compress.js 文件, 代码如下:
var fs = require("fs");
var zlib = require('zlib');

// 压缩 input.txt 文件为 input.txt.gz
fs.createReadStream('input.txt')
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream('input.txt.gz'));

console.log("文件压缩完成。");
代码执行结果如下:
$ node compress.js
文件压缩完成。
执行完以上操作后,我们可以看到当前目录下生成了 input.txt 的压缩文件 input.txt.gz。
接下来,让我们来解压该文件,创建 decompress.js 文件,代码如下:
var fs = require("fs");
var zlib = require('zlib');

// 解压 input.txt.gz 文件为 input.txt
fs.createReadStream('input.txt.gz')
.pipe(zlib.createGunzip())
.pipe(fs.createWriteStream('input.txt'));

console.log("文件解压完成。");
代码执行结果如下:
$ node decompress.js
文件解压完成。


16、Node.js模块系统

为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。
模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。


创建模块
在 Node.js 中,创建一个模块非常简单,如下我们创建一个 'main.js' 文件,代码如下:
var hello = require('./hello');
hello.world();
以上实例中,代码 require('./hello') 引入了当前目录下的hello.js文件(./ 为当前目录,node.js默认后缀为js)。
Node.js 提供了exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。
接下来我们就来创建hello.js文件,代码如下:
exports.world = function() {
console.log('Hello World');
}
在以上示例中,hello.js 通过 exports 对象把 world 作为模块的访问接口,在 main.js 中通过 require('./hello') 加载这个模块,
然后就可以直接访 问 hello.js 中 exports 对象的成员函数了。
有时候我们只是想把一个对象封装到模块中,格式如下:
module.exports = function() {
// ...
}
例如:
//hello.js
function Hello() {
var name;
this.setName = function(thyName) {
name = thyName;
};
this.sayHello = function() {
console.log('Hello ' + name);
};
};
module.exports = Hello;
这样就可以直接获得这个对象了:
//main.js
var Hello = require('./hello');
hello = new Hello();
hello.setName('BYVoid');
hello.sayHello();
模块接口的唯一变化是使用 module.exports = Hello 代替了
exports.world = function(){}。 在外部引用该模块时,其接口对象就是要输出
的 Hello 对象本身,而不是原先的 exports。

服务端的模块放在哪里
也许你已经注意到,我们已经在代码中使用了模块了。像这样:
var http = require("http");

...

http.createServer(...);
Node.js中自带了一个叫做"http"的模块,我们在我们的代码中请求它并把返回值赋给一个本地变量。
这把我们的本地变量变成了一个拥有所有 http 模块所提供的公共方法的对象。
Node.js 的 require方法中的文件查找策略如下:
由于Node.js中存在4类模块(原生模块和3种文件模块),尽管require方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。如下图所示:
nodejs-require
从文件模块缓存中加载
尽管原生模块与文件模块的优先级不同,但是都不会优先于从文件模块的缓存中加载已经存在的模块。
从原生模块加载
原生模块的优先级仅次于文件模块缓存的优先级。require方法在解析文件名之后,优先检查模块是否在原生模块列表中。以http模块为例,尽管在目录下存在一个http/http.js/http.node/http.json文件,require("http")都不会从这些文件中加载,而是从原生模块中加载。
原生模块也有一个缓存区,同样也是优先从缓存区加载。如果缓存区没有被加载过,则调用原生模块的加载方式进行加载和执行。
从文件加载
当文件模块缓存中不存在,而且不是原生模块的时候,Node.js会解析require方法传入的参数,并从文件系统中加载实际的文件,加载过程中的包装和编译细节在前一节中已经介绍过,这里我们将详细描述查找文件模块的过程,其中,也有一些细节值得知晓。
require方法接受以下几种参数的传递:
http、fs、path等,原生模块。
./mod或../mod,相对路径的文件模块。
/pathtomodule/mod,绝对路径的文件模块。
mod,非原生模块的文件模块。


17、

 

 

<div style="clear: both;height: 10%">
<div id="v_container" style="float: left;border: 1px solid;"></div>
<div style="float: right">
<input type="text" id="code_input" value="" placeholder="验证码"/>
<button id="my_button">验证</button>
</div>
</div>

posted on 2017-07-21 09:41  艾薇儿802388  阅读(203)  评论(0编辑  收藏  举报