环境配置
全篇教程来源:菜鸟教程 https://www.runoob.com/nodejs/nodejs-tutorial.html
安装
64 位安装包下载地址 : https://nodejs.org/dist/v4.4.3/node-v4.4.3-x64.msi
第一个应用
引入 required 模块
我们使用 require 指令来载入 http 模块,并将实例化的 HTTP 赋值给变量 http,实例如下:
1 | var http = require("http"); |
创建服务器
使用
http.createServer()
方法创建服务器,并使用listen
方法绑定8888
端口。 函数通过request
,response
参数来接收和响应数据。实例如下,在你项目的根目录下创建一个叫
server.js
的文件,并写入以下代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14var 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 命令执行以上的代码:
1
2node server.js
Server running at http://127.0.0.1:8888/接下来,打开浏览器访问 http://127.0.0.1:8888/,你会看到一个写着 “Hello World”的网页。
NPM
NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:
- 允许用户从NPM服务器下载别人编写的第三方包到本地使用。
- 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
- 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。
新版的nodejs已经集成了npm。
使用 npm 命令安装模块
npm 安装 Node.js 模块语法格式如下:
1 | $ npm install <Module Name> |
以下实例,我们使用 npm 命令安装常用的 Node.js web框架模块 express:
1 | npm install express # 本地安装 |
安装好之后,express 包就放在了工程目录下的 node_modules 目录中,因此在代码中只需要通过 require(‘express’) 的方式就好,无需指定第三方包路径。
1 | var express = require('express'); |
Node.js REPL(交互式解释器)
Node.js REPL(Read Eval Print Loop:交互式解释器) 表示一个电脑的环境,类似 Window 系统的终端或 Unix/Linux shell,我们可以在终端中输入命令,并接收系统的响应。
REPL 语法
启动 Node 的终端
1 | $ node |
这时就可以在 > 后输入简单的表达式,并按下回车键来计算结果。
简单的表达式运算
在 Node.js REPL 的命令行窗口中执行简单的数学运算:
1 | $ node |
使用变量
变量声明需要使用 var 关键字,如果没有使用 var 关键字变量会直接打印出来。
使用 var 关键字的变量可以使用 console.log() 来输出变量。
1 | $ node |
多行表达式
Node REPL 支持输入多行表达式,这就有点类似 JavaScript。接下来让我们来执行一个 do-while 循环:
1 | $ node |
… 三个点的符号是系统自动生成的,回车换行后即可。Node 会自动检测是否为连续的表达式。
下划线(_)变量
使用下划线(_)获取上一个表达式的运算结果
1 | $ node |
REPL 命令
- ctrl + c - 退出当前终端,可能会提示 .exit
- ctrl + c 按下两次 - 退出 Node REPL。
- ctrl + d - 退出 Node REPL.
- 向上/向下 键 - 查看输入的历史命令
- tab 键 - 列出当前命令
- .help - 列出使用命令
- .break - 退出多行表达式
- .clear - 退出多行表达式
- .save filename - 保存当前的 Node REPL 会话到指定文件
- .load filename - 载入当前 Node REPL 会话的文件内容。
方法
Node.js 回调函数
Node.js 异步编程的直接体现就是回调 ,其所有 API 都支持回调函数。
阻塞代码实例
main.js 文件代码:
1 | var fs = require("fs"); // file stream 模块? |
运行程序:
1 | $ node main.js |
非阻塞代码实例
main.js 文件代码:
1 | var fs = require("fs"); |
Node.js 事件
通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:
1 | var events = require('events'); // 引入 events 模块 |
以下程序绑定事件处理程序:
1 | eventEmitter.on('eventName', eventHandler); // 绑定事件及事件的处理程序 |
我们可以通过程序触发事件:
1 | eventEmitter.emit('eventName'); // 触发事件 |
绑定示例:
1 | var events = require('events'); |
EventEmitter 类
events 模块只提供了一个对象:events.EventEmitter
。EventEmitter 的核心就是事件触发与事件监听器功能的封装。
EventEmitter 对象如果在实例化时发生错误,会触发 error 事件。当添加新的监听器时,newListener 事件会触发,当监听器被移除时,removeListener 事件被触发。
1 | var EventEmitter = require('events').EventEmitter; |
1 | var events = require('events'); |
运行结果:两个事件监听器回调函数被先后调用。
EventEmitter 的属性
addListener(event, listener)
为指定事件添加一个监听器到监听器数组的尾部。on(event, listener)
为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。
1 | server.on('ev', function (stream) { ; }); |
once(event, listener)
为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器
1 | server.once('ev', function (stream) { ; }); |
removeListener(event, listener)
移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。它接受两个参数,第一个是事件名称,第二个是回调函数名称。
1 | server.removeListener('ev', callback); |
removeAllListeners([event])
移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。setMaxListeners(n)
默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。listeners(event)
返回指定事件的监听器数组。emit(event, [arg1], [arg2], [...])
按参数的顺序执行每个监听器,如果事件有注册监听返回 true,否则返回 false。
类方法
listenerCount(emitter, event)
返回指定事件的监听器数量。
事件
newListener
- event - 字符串,事件名称
- listener- 处理事件函数该事件在添加新监听器时被触发。
removeListener
- event - 字符串,事件名称
- listener - 处理事件函数从指定监听器数组中删除一个监听器。需要注意的是,此操作将会改变处于被删监听器之后的那些监听器的索引。
实例:获取特定事件数量
1 | var events = require('events'); |
error 事件
如果没有响应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。
一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。
继承 EventEmitter
大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
数据
Node.js Buffer 缓冲区
JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。
但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。
Buffer 与字符编码
Buffer 实例一般用于表示编码字符的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六进制编码的数据。 通过使用显式的字符编码,就可以在 Buffer 实例与普通的 JavaScript 字符串之间进行相互转换。
1 | const buf = Buffer.from('runoob', 'ascii'); |
Node.js 目前支持的字符编码包括:
- ascii - 仅支持 7 位 ASCII 数据。如果设置去掉高位的话,这种编码是非常快的。
- utf8 - 多字节编码的 Unicode 字符。许多网页和其他文档格式都使用 UTF-8 。
- utf16le - 2 或 4 个字节,小字节序编码的 Unicode 字符。支持代理对(U+10000 至 U+10FFFF)。
- ucs2 - utf16le 的别名。
- base64 - Base64 编码。
- latin1 - 一种把 Buffer 编码成一字节编码的字符串的方式。
- binary - latin1 的别名。
- hex - 将每个字节编码为两个十六进制字符。
创建 Buffer 类
Buffer 提供了以下 API 来创建 Buffer 类:
- Buffer.alloc(size[, fill[, encoding]]): 返回一个指定大小的 Buffer 实例,如果没有设置 fill,则默认填满 0
- Buffer.allocUnsafe(size): 返回一个指定大小的 Buffer 实例,但是它不会被初始化,所以它可能包含敏感的数据
- Buffer.allocUnsafeSlow(size)
- Buffer.from(array): 返回一个被 array 的值初始化的新的 Buffer 实例(传入的 array 的元素只能是数字,不然就会自动被 0 覆盖)
- Buffer.from(arrayBuffer[, byteOffset[, length]]): 返回一个新建的与给定的 ArrayBuffer 共享同一内存的 Buffer。
- Buffer.from(buffer): 复制传入的 Buffer 实例的数据,并返回一个新的 Buffer 实例
- Buffer.from(string[, encoding]): 返回一个被 string 的值初始化的新的 Buffer 实例
1 | // 创建一个长度为 10、且用 0 填充(默认)的 Buffer。 |
写入缓冲区
1 | buf.write(string[, offset[, length]][, encoding]) |
- string - 写入缓冲区的字符串。
- offset - 缓冲区开始写入的索引值,默认为 0 。
- length - 写入的字节数,默认为 buffer.length
- encoding - 使用的编码。默认为 ‘utf8’ 。
根据 encoding 的字符编码写入 string 到 buf 中的 offset 位置。如果 buf 没有足够的空间保存整个字符串,则只会写入 string 的一部分。 只部分解码的字符不会被写入。
返回值:返回实际写入的大小。如果 buffer 空间不足, 则只会写入部分字符串。
1 | buf = Buffer.alloc(256); |
从缓冲区读取数据
1 | buf.toString([encoding[, start[, end]]]) |
- encoding - 使用的编码。默认为 ‘utf8’ 。
- start - 指定开始读取的索引位置,默认为 0。
- end - 结束位置,默认为缓冲区的末尾。
返回值:解码缓冲区数据并使用指定的编码返回字符串。
1 | buf = Buffer.alloc(26); |
将 Buffer 转换为 JSON 对象
1 | buf.toJSON() |
当字符串化一个 Buffer 实例时,JSON.stringify() 会隐式地调用该 toJSON()。
返回值:返回 JSON 对象。
1 | const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]); |
缓冲区合并
1 | Buffer.concat(list[, totalLength]) |
- list - 用于合并的 Buffer 对象数组列表。
- totalLength - 指定合并后Buffer对象的总长度。
返回值:返回一个多个成员合并的新 Buffer 对象。
1 | var buffer1 = Buffer.from(('Hello ')); |
缓冲区比较
1 | buf.compare(otherBuffer); |
- otherBuffer - 与 buf 对象比较的另外一个 Buffer 对象。
返回值:返回一个数字,表示 buf 在 otherBuffer 之前,之后或相同。
1 | var buffer1 = Buffer.from('ABC'); |
拷贝缓冲区
1 | buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]]) |
- targetBuffer - 要拷贝的 Buffer 对象。
- targetStart - 数字, 可选, 默认: 0
- sourceStart - 数字, 可选, 默认: 0
- sourceEnd - 数字, 可选, 默认: buffer.length
返回值:没有返回值。
1 | var buf1 = Buffer.from('abcdefghijkl'); |
缓冲区裁剪
1 | buf.slice([start[, end]]) |
- start - 数字, 可选, 默认: 0
- end - 数字, 可选, 默认: buffer.length
返回值:返回一个新的缓冲区,它和旧缓冲区指向同一块内存,但是从索引 start 到 end 的位置剪切。
1 | var buffer1 = Buffer.from('hello'); |
缓冲区长度
1 | buf.length; |
返回值:返回 Buffer 对象所占据的内存长度。
1 | var buffer = Buffer.from('www.runoob.com'); |
Node.js Stream
Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。
Node.js,Stream 有四种流类型:
- Readable - 可读操作。
- Writable - 可写操作。
- Duplex - 可读可写操作.
- Transform - 操作被写入数据,然后读出结果。
所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:
- data - 当有数据可读时触发。
- end - 没有更多的数据可读时触发。
- error - 在接收和写入过程中发生错误时触发。
- finish - 所有数据已被写入到底层系统时触发。
从流中读取数据
1 | var fs = require("fs"); // file stream 的缩写吧 |
写入流
1 | var fs = require("fs"); |
管道流
管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。
以下实例通过读取一个文件内容并将内容写入到另外一个文件中。:
1 | var fs = require("fs"); |
链式流
链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。
用管道和链式来压缩和解压文件:
1 | var fs = require("fs"); |
执行完以上操作后,我们可以看到当前目录下生成了 input.txt 的压缩文件 input.txt.gz。
接下来,反过来解压该文件,代码如下:
1 | var fs = require("fs"); |
流程
Node.js 模块系统
模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。
创建模块
在 Node.js 中,创建一个模块非常简单,如下我们创建一个 main.js 文件,代码如下:
1 | var hello = require('./hello'); |
以上实例中,代码 require('./hello')
引入了当前目录下的 hello.js 文件(./
为当前目录,node.js 默认后缀为 js)。
Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。
接下来我们就来创建 hello.js 文件,代码如下:
1 | exports.world = function() { |
在以上示例中,hello.js 通过 exports 对象把 world 作为模块的访问接口,在 main.js 中通过 require(‘./hello’) 加载这个模块,然后就可以直接访 问 hello.js 中 exports 对象的成员函数了。
封装对象到模块
有时候我们只是想把一个对象封装到模块中,格式如下:
1 | module.exports = function() { |
例如:
hello.js
1 | function Hello() { |
这样就可以直接获得这个对象了:
main.js
1 | var Hello = require('./hello'); |
模块接口的唯一变化是使用 module.exports = Hello
代替了exports.world = function(){}
。 在外部引用该模块时,其接口对象就是要输出的 Hello
对象本身,而不是原先的 exports
。
服务端模块
Node.js 中自带了一个叫做 http 的模块。
模块加载顺序:文件模块缓存 -> 原生模块 -> 文件
Node.js 函数
一个函数可以作为另一个函数的参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。例如:
1 | function say(word) { |
匿名函数
把一个函数作为变量传递,绕开“先定义,再传递”
1 | function execute(someFunction, value) { |
Node.js 路由
我们要为路由提供请求的 URL 和其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码。
我们需要的所有数据都会包含在 request 对象中,该对象作为 onRequest() 回调函数的第一个参数传递。但是为了解析这些数据,我们需要额外的 Node.JS 模块,它们分别是 url 和 querystring 模块。
找出浏览器请求的 URL 路径:
server.js
1 | var http = require("http"); |
router.js
1 | function route(pathname) { |
index.js
1 | var server = require("./server"); |
$ node index.js
,浏览器访问 http://127.0.0.1:8888
输出会有比较烦人的 /favicon.ico 请求相关的部分(可无视)
Node.js 全局对象
在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局对象是 global,所有全局变量(除了 global 本身以外)都是 global 对象的属性。global 最根本的作用是作为全局变量的宿主。
当你定义一个全局变量时,这个变量同时也会成为全局对象的属性,反之亦然。需要注意的是,在 Node.js 中你不可能在最外层定义变量,因为所有用户代码都是属于当前模块的, 而模块本身不是最外层上下文。
注意: 最好不要使用 var 定义变量以避免引入全局变量,因为全局变量会污染命名空间,提高代码的耦合风险。
__filename
当前正在执行的脚本的文件名。输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。
__dirname
当前执行脚本所在的目录。
setTimeout(cb, ms)
在指定的毫秒(ms)数后执行指定函数(cb)。只执行一次指定函数。
返回一个代表定时器的句柄值。
clearTimeout(t)
用于停止一个之前通过 setTimeout() 创建的定时器。 参数 t 是通过 setTimeout() 函数创建的定时器。
1
2
3
4
5
6function printHello(){
console.log( "Hello, World!");
}
var t = setTimeout(printHello, 2000); // 两秒后执行以上函数
clearTimeout(t); // 清除定时器setInterval(cb, ms)
在指定的毫秒(ms)数后执行指定函数(cb)。会不停地调用,直到清除或关闭。
返回一个代表定时器的句柄值。可以使用 clearInterval(t) 函数来清除定时器。
console
提供控制台标准输出:log / info / error / warn / dir / time / timeEnd / trace / assert
process
描述当前Node.js 进程状态的对象,提供了一个与操作系统的简单接口。
exit / beforeExit / uncaughtException / Signal 事件
退出状态码、属性、方法:菜鸟教程
工具
Node.js 常用工具
util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaScript 的功能 过于精简的不足。
util.inherits
实现对象间原型继承的函数
JavaScript 的面向对象特性是基于原型的,与常见的基于类的不同。JavaScript 没有提供对象继承的语言级别特性,而是通过原型复制来实现的。
1 | var util = require('util'); |
我们定义了一个基础对象 Base 和一个继承自 Base 的 Sub,Base 有三个在构造函数内定义的属性和一个原型中定义的函数,通过util.inherits 实现继承。
注意:Sub 仅仅继承了Base 在原型中定义的函数,而构造函数内部创造的 base 属 性和 sayHello 函数都没有被 Sub 继承。
同时,在原型中定义的属性不会被 console.log 作 为对象的属性输出。如果我们去掉 objSub.sayHello(); 这行的注释,将会看到:
1 | node.js:201 |
util.inspect
util.inspect(object,[showHidden],[depth],[colors]) 是一个将任意对象转换 为字符串的方法,通常用于调试和错误输出。它至少接受一个参数 object,即要转换的对象。
showHidden
是一个可选参数,如果值为 true,将会输出更多隐藏信息。depth
表示最大递归的层数,如果对象很复杂,你可以指定层数以控制输出信息的多 少。如果不指定depth,默认会递归2层,指定为 null 表示将不限递归层数完整遍历对象。 如果color 值为 true,输出格式将会以ANSI 颜色编码,通常用于在终端显示更漂亮 的效果。
特别要指出的是,util.inspect
并不会简单地直接把对象转换为字符串,即使该对象定义了 toString
方法也不会调用。
1 | var util = require('util'); |
运行结果是:
1 | Person { name: 'byvoid', toString: [Function] } |