环境配置

全篇教程来源:菜鸟教程 https://www.runoob.com/nodejs/nodejs-tutorial.html

安装

64 位安装包下载地址 : https://nodejs.org/dist/v4.4.3/node-v4.4.3-x64.msi

第一个应用

  1. 引入 required 模块

    我们使用 require 指令来载入 http 模块,并将实例化的 HTTP 赋值给变量 http,实例如下:

1
var http = require("http");
  1. 创建服务器

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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 命令执行以上的代码:

    1
    2
    node server.js
    Server running at http://127.0.0.1:8888/
  2. 接下来,打开浏览器访问 http://127.0.0.1:8888/,你会看到一个写着 “Hello World”的网页。

NPM

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

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

新版的nodejs已经集成了npm。

npm安装教程:https://www.runoob.com/nodejs/nodejs-npm.html

使用 npm 命令安装模块

npm 安装 Node.js 模块语法格式如下:

1
$ npm install <Module Name>

以下实例,我们使用 npm 命令安装常用的 Node.js web框架模块 express:

1
2
npm install express      # 本地安装
npm install express -g # 全局安装

安装好之后,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
2
$ node
>

这时就可以在 > 后输入简单的表达式,并按下回车键来计算结果。

简单的表达式运算

在 Node.js REPL 的命令行窗口中执行简单的数学运算:

1
2
3
4
5
$ node
> 5 / 2
2.5
> 1 + ( 2 * 3 ) - 4
3

使用变量

变量声明需要使用 var 关键字,如果没有使用 var 关键字变量会直接打印出来。

使用 var 关键字的变量可以使用 console.log() 来输出变量。

1
2
3
4
5
6
7
8
9
10
$ node
> x = 10
10
> var y = 10
undefined
> x + y
20
> console.log("Hello World")
Hello World
undefined

多行表达式

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

1
2
3
4
5
6
7
8
9
10
11
$ node
> var x = 0
undefined
> do {
... x++;
... console.log("x: " + x);
... } while ( x < 3 );
x: 1
x: 2
x: 3
undefined

三个点的符号是系统自动生成的,回车换行后即可。Node 会自动检测是否为连续的表达式。

下划线(_)变量

使用下划线(_)获取上一个表达式的运算结果

1
2
3
4
5
6
$ node
> 10 + 20
30
> console.log(_ + _)
60
undefined

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
2
3
4
var fs = require("fs"); // file stream 模块?
var data = fs.readFileSync('input.txt');
console.log(data.toString());
console.log("程序执行结束!");

运行程序:

1
$ node main.js

非阻塞代码实例

main.js 文件代码:

1
2
3
4
5
6
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});
console.log("程序执行结束!");

Node.js 事件

通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:

1
2
var events = require('events');               // 引入 events 模块
var eventEmitter = new events.EventEmitter(); // 创建 eventEmitter 对象

以下程序绑定事件处理程序:

1
eventEmitter.on('eventName', eventHandler);   // 绑定事件及事件的处理程序

我们可以通过程序触发事件:

1
eventEmitter.emit('eventName'); // 触发事件

绑定示例:

1
2
3
4
5
6
7
8
9
10
var events = require('events');
var eventEmitter = new events.EventEmitter();

var connectHandler = function connected() { // 创建事件处理程序
console.log('连接成功。');
}

eventEmitter.on('connection', connectHandler); // 绑定 connection 事件处理程序

eventEmitter.emit('connection'); // 触发 connection 事件

EventEmitter 类

events 模块只提供了一个对象:events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。

EventEmitter 对象如果在实例化时发生错误,会触发 error 事件。当添加新的监听器时,newListener 事件会触发,当监听器被移除时,removeListener 事件被触发。

1
2
3
4
5
6
7
8
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();
event.on('ev', function() {
console.log('ev 事件触发');
});
setTimeout(function() {
event.emit('ev');
}, 1000); // 定时1秒
1
2
3
4
5
6
7
8
9
var events = require('events'); 
var emitter = new events.EventEmitter();
emitter.on('ev', function(arg1, arg2) {
console.log('listener1', arg1, arg2);
});
emitter.on('ev', function(arg1, arg2) {
console.log('listener2', arg1, arg2);
});
emitter.emit('ev', 'arg1 参数', 'arg2 参数');

运行结果:两个事件监听器回调函数被先后调用。

EventEmitter 的属性

  1. addListener(event, listener) 为指定事件添加一个监听器到监听器数组的尾部。
  2. on(event, listener) 为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。
1
server.on('ev', function (stream) { ; });
  1. once(event, listener) 为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器
1
server.once('ev', function (stream) { ; });
  1. removeListener(event, listener) 移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。它接受两个参数,第一个是事件名称,第二个是回调函数名称。
1
server.removeListener('ev', callback);
  1. removeAllListeners([event]) 移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。
  2. setMaxListeners(n) 默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。
  3. listeners(event) 返回指定事件的监听器数组。
  4. emit(event, [arg1], [arg2], [...]) 按参数的顺序执行每个监听器,如果事件有注册监听返回 true,否则返回 false。

类方法

  1. listenerCount(emitter, event) 返回指定事件的监听器数量。

事件

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

实例:获取特定事件数量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var events = require('events');
var eventEmitter = new events.EventEmitter();

var listener1 = function listener1() {;}
var listener2 = function listener2() {;}

// addListener 和 on 效果一样的
eventEmitter.addListener('connection', listener1);
eventEmitter.on('connection', listener2);

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

eventEmitter.removeListener('connection', listener1);

eventEmitter.emit('connection');

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
2
3
const buf = Buffer.from('runoob', 'ascii');
console.log(buf.toString('hex')); // 输出 72756e6f6f62
console.log(buf.toString('base64')); // 输出 cnVub29i

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建一个长度为 10、且用 0 填充(默认)的 Buffer。
const buf1 = Buffer.alloc(10);

// 创建一个长度为 10、且用 0x1 填充的 Buffer。
const buf2 = Buffer.alloc(10, 1);

// 创建一个长度为 10、且未初始化的 Buffer,这个方法比调用 Buffer.alloc() 更快,
// 但返回的 Buffer 实例可能包含旧数据,因此需要使用 fill() 或 write() 重写。
const buf3 = Buffer.allocUnsafe(10);

// 创建一个包含 [0x1, 0x2, 0x3] 的 Buffer。
const buf4 = Buffer.from([1, 2, 3]);

// 创建一个包含 UTF-8 字节 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。
const buf5 = Buffer.from('tést');

// 创建一个包含 Latin-1 字节 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。
const buf6 = Buffer.from('tést', 'latin1');

写入缓冲区

1
buf.write(string[, offset[, length]][, encoding])
  • string - 写入缓冲区的字符串。
  • offset - 缓冲区开始写入的索引值,默认为 0 。
  • length - 写入的字节数,默认为 buffer.length
  • encoding - 使用的编码。默认为 ‘utf8’ 。

根据 encoding 的字符编码写入 string 到 buf 中的 offset 位置。如果 buf 没有足够的空间保存整个字符串,则只会写入 string 的一部分。 只部分解码的字符不会被写入。

返回值:返回实际写入的大小。如果 buffer 空间不足, 则只会写入部分字符串。

1
2
buf = Buffer.alloc(256);
len = buf.write("www.runoob.com"); // 14

从缓冲区读取数据

1
buf.toString([encoding[, start[, end]]])
  • encoding - 使用的编码。默认为 ‘utf8’ 。
  • start - 指定开始读取的索引位置,默认为 0。
  • end - 结束位置,默认为缓冲区的末尾。

返回值:解码缓冲区数据并使用指定的编码返回字符串。

1
2
3
4
5
6
7
8
9
buf = Buffer.alloc(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

将 Buffer 转换为 JSON 对象

1
buf.toJSON()

当字符串化一个 Buffer 实例时,JSON.stringify() 会隐式地调用该 toJSON()。

返回值:返回 JSON 对象。

1
2
3
4
5
6
7
8
9
10
const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
const json = JSON.stringify(buf);

console.log(json); // 输出: {"type":"Buffer","data":[1,2,3,4,5]}

const copy = JSON.parse(json, (key, value) => {
return value && value.type === 'Buffer' ? Buffer.from(value.data) : value;
});

console.log(copy); // 输出: <Buffer 01 02 03 04 05>

缓冲区合并

1
Buffer.concat(list[, totalLength])
  • list - 用于合并的 Buffer 对象数组列表。
  • totalLength - 指定合并后Buffer对象的总长度。

返回值:返回一个多个成员合并的新 Buffer 对象。

1
2
3
4
var buffer1 = Buffer.from(('Hello '));
var buffer2 = Buffer.from(('World!'));
var buffer3 = Buffer.concat([buffer1,buffer2]);
console.log("buffer3: " + buffer3.toString()); // buffer3: Hello World!

缓冲区比较

1
buf.compare(otherBuffer);
  • otherBuffer - 与 buf 对象比较的另外一个 Buffer 对象。

返回值:返回一个数字,表示 bufotherBuffer 之前,之后或相同。

1
2
3
4
5
6
7
var buffer1 = Buffer.from('ABC');
var buffer2 = Buffer.from('ABCD');
var result = buffer1.compare(buffer2);

if(result < 0) {
console.log(buffer1 + " 在 " + buffer2 + "之前");
}else if(result == 0){ }

拷贝缓冲区

1
buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
  • targetBuffer - 要拷贝的 Buffer 对象。
  • targetStart - 数字, 可选, 默认: 0
  • sourceStart - 数字, 可选, 默认: 0
  • sourceEnd - 数字, 可选, 默认: buffer.length

返回值:没有返回值。

1
2
3
4
var buf1 = Buffer.from('abcdefghijkl');
var buf2 = Buffer.from('RUNOOB');
buf2.copy(buf1, 2); //将 buf2 插入到 buf1 指定位置上
console.log(buf1.toString()); // abRUNOOBijkl

缓冲区裁剪

1
buf.slice([start[, end]])
  • start - 数字, 可选, 默认: 0
  • end - 数字, 可选, 默认: buffer.length

返回值:返回一个新的缓冲区,它和旧缓冲区指向同一块内存,但是从索引 start 到 end 的位置剪切。

1
2
3
var buffer1 = Buffer.from('hello');
var buffer2 = buffer1.slice(0,2); // 剪切缓冲区
console.log("buffer2: " + buffer2.toString()); // he

缓冲区长度

1
buf.length;

返回值:返回 Buffer 对象所占据的内存长度。

1
2
var buffer = Buffer.from('www.runoob.com');
console.log("buffer length: " + buffer.length); // 14

Node.js Stream

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

Node.js,Stream 有四种流类型:

  • Readable - 可读操作。
  • Writable - 可写操作。
  • Duplex - 可读可写操作.
  • Transform - 操作被写入数据,然后读出结果。

所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:

  • data - 当有数据可读时触发。
  • end - 没有更多的数据可读时触发。
  • error - 在接收和写入过程中发生错误时触发。
  • finish - 所有数据已被写入到底层系统时触发。

从流中读取数据

1
2
3
4
5
6
7
8
9
10
var fs = require("fs"); // file stream 的缩写吧
var data = ''; // 存储读出的内容
var readerStream = fs.createReadStream('input.txt'); // 创建可读流

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

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

写入流

1
2
3
4
5
6
7
8
9
var fs = require("fs");
var data = '菜鸟教程官网地址:www.runoob.com';
var writerStream = fs.createWriteStream('output.txt'); // 创建一个可以写入的流

writerStream.write(data,'UTF8'); // 使用 utf8 编码写入数据
writerStream.end(); // 标记文件末尾

writerStream.on('finish', function() { console.log("写入完成。"); } );
writerStream.on('error', function(err){ console.log(err.stack); } );

管道流

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

以下实例通过读取一个文件内容并将内容写入到另外一个文件中。:

1
2
3
4
5
6
var fs = require("fs");
var readerStream = fs.createReadStream('input.txt'); // 创建一个可读流
var writerStream = fs.createWriteStream('output.txt'); // 创建一个可写流

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

链式流

链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。

用管道和链式来压缩和解压文件:

1
2
3
4
5
6
7
8
9
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("文件压缩完成。");

执行完以上操作后,我们可以看到当前目录下生成了 input.txt 的压缩文件 input.txt.gz。

接下来,反过来解压该文件,代码如下:

1
2
3
4
5
6
7
8
9
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.js 模块系统

模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。

创建模块

在 Node.js 中,创建一个模块非常简单,如下我们创建一个 main.js 文件,代码如下:

1
2
var hello = require('./hello');
hello.world();

以上实例中,代码 require('./hello') 引入了当前目录下的 hello.js 文件(./ 为当前目录,node.js 默认后缀为 js)。

Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。

接下来我们就来创建 hello.js 文件,代码如下:

1
2
3
exports.world = function() {
console.log('Hello World');
}

在以上示例中,hello.js 通过 exports 对象把 world 作为模块的访问接口,在 main.js 中通过 require(‘./hello’) 加载这个模块,然后就可以直接访 问 hello.js 中 exports 对象的成员函数了。

封装对象到模块

有时候我们只是想把一个对象封装到模块中,格式如下:

1
2
3
module.exports = function() {
// ...
}

例如:

hello.js

1
2
3
4
5
6
7
8
9
10
function Hello() { 
var name;
this.setName = function(thyName) {
name = thyName;
};
this.sayHello = function() {
console.log('Hello ' + name);
};
};
module.exports = Hello;

这样就可以直接获得这个对象了:

main.js

1
2
3
4
var Hello = require('./hello'); 
hello = new Hello();
hello.setName('BYVoid');
hello.sayHello();

模块接口的唯一变化是使用 module.exports = Hello 代替了exports.world = function(){}。 在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports

服务端模块

Node.js 中自带了一个叫做 http 的模块。

模块加载顺序:文件模块缓存 -> 原生模块 -> 文件

Node.js 函数

一个函数可以作为另一个函数的参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。例如:

1
2
3
4
5
6
7
8
9
function say(word) {
console.log(word);
}

function execute(someFunction, value) {
someFunction(value);
}

execute(say, "Hello");

匿名函数

把一个函数作为变量传递,绕开“先定义,再传递”

1
2
3
4
5
function execute(someFunction, value) {
someFunction(value);
}

execute(function(word){ console.log(word) }, "Hello");

Node.js 路由

我们要为路由提供请求的 URL 和其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码。

我们需要的所有数据都会包含在 request 对象中,该对象作为 onRequest() 回调函数的第一个参数传递。但是为了解析这些数据,我们需要额外的 Node.JS 模块,它们分别是 url 和 querystring 模块。

找出浏览器请求的 URL 路径:

server.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var http = require("http");
var url = require("url");

function start(route) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");

route(pathname);

response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}

http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}

exports.start = start;

router.js

1
2
3
4
5
function route(pathname) {
console.log("About to route a request for " + pathname);
}

exports.route = route;

index.js

1
2
3
4
var server = require("./server");
var router = require("./router");

server.start(router.route);

$ 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
    6
    function 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 objBase = new Base();
objBase.showName(); // base
objBase.sayHello(); // Hello base
console.log(objBase); // { name: 'base', base: 1991, sayHello: [Function] }
var objSub = new Sub();
objSub.showName(); // sub
//objSub.sayHello(); // (构造函数中的不会继承)
console.log(objSub); { name: 'sub' }

我们定义了一个基础对象 Base 和一个继承自 Base 的 Sub,Base 有三个在构造函数内定义的属性和一个原型中定义的函数,通过util.inherits 实现继承。

注意:Sub 仅仅继承了Base 在原型中定义的函数,而构造函数内部创造的 base 属 性和 sayHello 函数都没有被 Sub 继承。

同时,在原型中定义的属性不会被 console.log 作 为对象的属性输出。如果我们去掉 objSub.sayHello(); 这行的注释,将会看到:

1
2
3
4
5
6
7
8
9
10
11
node.js:201 
throw e; // process.nextTick error, or 'error' event on first tick
^
TypeError: Object #&lt;Sub&gt; has no method 'sayHello'
at Object.&lt;anonymous&gt; (/home/byvoid/utilinherits.js:29:8)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
at Module.load (module.js:348:31)
at Function._load (module.js:308:12)
at Array.0 (module.js:479:10)
at EventEmitter._tickCallback (node.js:192:40)

util.inspect

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

  • showHidden 是一个可选参数,如果值为 true,将会输出更多隐藏信息。
  • depth 表示最大递归的层数,如果对象很复杂,你可以指定层数以控制输出信息的多 少。如果不指定depth,默认会递归2层,指定为 null 表示将不限递归层数完整遍历对象。 如果color 值为 true,输出格式将会以ANSI 颜色编码,通常用于在终端显示更漂亮 的效果。

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

1
2
3
4
5
6
7
8
9
10
var util = require('util'); 
function Person() {
this.name = 'byvoid';
this.toString = function() {
return this.name;
};
}
var obj = new Person();
console.log(util.inspect(obj));
console.log(util.inspect(obj, true));

运行结果是:

1
2
3
4
5
6
7
8
9
10
Person { name: 'byvoid', toString: [Function] }
Person {
name: 'byvoid',
toString:
{ [Function]
[length]: 0,
[name]: '',
[arguments]: null,
[caller]: null,
[prototype]: { [constructor]: [Circular] } } }