nodejs笔记
简介
简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
安装
NPM使用介绍
新版的nodejs已经集成了npm,可以通过查看版本号测试是否安装成功:
$ npm -v
4.2.0
使用淘宝NPM镜像
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
// 使用cnpm来安装模块
$ cnpm install [name]
安装模块
$ npm install <Module Name> #本地
$ npm install <Module Name> -g #全局
查看安装信息
$ npm list -g
查看某个模块的版本号
$ npm list grunt
卸载模块
$ npm uninstall express
更新模块
$ npm update express
搜索模块
$ npm search express
创建模块,会生成 package.json 文件
$ npm init
版本号
语义版本号分为X.Y.Z三位,分别代表主版本号、次版本号和补丁版本号。
- 如果只是修复bug,需要更新Z位。
- 如果是新增了功能,但是向下兼容,需要更新Y位。
- 如果有大变动,向下不兼容,需要更新X位。
回调函数
阻塞代码
var fs = require("fs");
var data = fs.readFileSync('input.txt');
console.log(data.toString());
console.log("程序执行结束!");
非阻塞代码
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});
console.log("程序执行结束!");
阻塞实例在文件读取完后才执行完程序。
非阻塞实例我们不需要等待文件读取完,这样就可以在读取文件时同时执行接下来的代码,大大提高了程序的性能。
阻塞是按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,我们就需要写在回调函数内。
事件循环
- nodejs是单进程单线程应用程序,通过事件和回调支持并发,所以性能非常高。
- nodejs的每一个API都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发。
- nodejs基本上所有的事件机制都是用设计模式中的观察者模式实现。
- nodejs单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数。
var events = require('events');
var eventEmitter = new events.EventEmitter();
// 监听器1
var listener1 = function listener1(){
console.log('监听器 listener1 执行。');
}
// 监听器2
var listener2 = function listener2(){
console.log('监听器 lisrener2 执行。');
}
// 绑定连接事件,处理函数为listener1
eventEmitter.addListener('connection',listener1);
// 绑定连接事件,处理函数为listener1
eventEmitter.addListener('connection',listener2);
var eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + ' 个监听器监听连接事件。');
// 处理连接事件
eventEmitter.emit('connection');
// 移除监绑定的 listener1 函数
eventEmitter.removeListener('connection',listener1);
console.log('listener1 不再受监听。');
// 触发连接事件
eventEmitter.emit('connection');
eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + ' 个监听器监听连接事件。');
eventEmitter.emit('error');
console.log('程序执行完成。');
// 执行结果
$ node main.js
2 个监听器监听连接事件。
监听器 listener1 执行。
监听器 listener2 执行。
listener1 不再受监听。
监听器 listener2 执行。
1 个监听器监听连接事件。
程序执行完毕。
Buffer(缓冲区)
在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。
创建Buffer类
var buf = new Buffer([10,20,30,40,50]);;
console.log(buf);
// <Buffer 0a 14 1e 28 32>
var buf2 = new Buffer('www.runoob.com','utf-8');
console.log(buf2);
// <Buffer 77 77 77 2e 72 75 6e 6f 6f 62 2e 63 6f 6d>
var buf3 = new Buffer(10);
console.log(buf3);
// <Buffer 00 00 00 00 00 00 00 00 00 00>
写入缓冲区
var len = buf3.write('www.runoob.com');
console.log('写入字节数:' + len);
从缓冲区读取数据
console.log(buf.toString('ascii'));
console.log(buf.toString('ascii',0,5));
console.log(buf.toString('utf8',0,5));
console.log(buf.toString(undefined,0,5));
console.log(buf.toJSON());
缓冲区合并
var buffer1 = new Buffer('菜鸟教程');
var buffer2 = new Buffer('www.runoob.com');
var buffer3 = Buffer.concat([buffer1,buffer2]);
console.log('buffer3 内容: '+buffer3.toString());
缓冲区比较
var buff1 = new Buffer('ABC');
var buff2 = new Buffer('ABCD');
var result = buff1.compare(buff2);
if(result < 0){
console.log(buff1 + ' 在 ' + buff2 + '之前');
}else if(result == 0){
console.log(buff1 + ' 与 ' + buff2 + '相同');
}else{
console.log(buff1 + ' 在 ' + buff2 + '之后');
}
拷贝缓冲区
var b1 = new Buffer('ABC');
var b2 = new Buffer(3);
b1.copy(b2);
console.log('b2 content: '+b2.toString());
缓冲区裁剪
var buffer1 = new Buffer('runoob');
// 剪切缓冲区
var buffer2 = buffer1.slice(0,2);
console.log("buffer2 content: " + buffer2.toString());
缓冲区长度
var buffer = new Buffer('www.runoob.com');
// 缓冲区长度
console.log("buffer length: " + buffer.length);
Stream(流)
Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。
Node.js,Stream 有四种流类型:
- Readable - 可读操作。
- Writable - 可写操作。
- Duplex - 可读可写操作.
- Transform - 操作被写入数据,然后读出结果。
所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:
- data - 当有数据可读时触发。
- end - 没有更多的数据可读时触发。
- error - 在接收和写入过程中发生错误时触发。
- finish - 所有数据已被写入到底层系统时触发。
从流中读取数据
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("程序执行完毕");
写入流
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("程序执行完毕");
管道流,提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中,实现了大文件的复制过程。
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("程序执行完毕");
链式流,通过连接输出流到另外一个流并创建多个对个流操作链的机制。链式流一般用于管道操作。用管道和链式来压缩和解压文件。
压缩文件
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("文件压缩完成。");
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("文件解压完成。");
模块系统
模块公开的接口
exports.world = function() {
console.log('Hello World');
}
// 调用方式
var hello = require('./hello');
hello.world();
模块接口
//hello.js
function Hello() {
var name;
this.setName = function(thyName) {
name = thyName;
};
this.sayHello = function() {
console.log('Hello ' + name);
};
};
module.exports = Hello;
// 调用方式
var Hello = require('./hello');
hello = new Hello();
hello.setName('BYVoid');
hello.sayHello();
模块接口的唯一变化是使用 module.exports = Hello 代替了exports.world = function(){}。 在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports。
函数
一个函数可以作为另一个函数的参数
function say(word) {
console.log(word);
}
function execute(someFunction, value) {
someFunction(value);
}
execute(say, "Hello");
匿名函数
function execute(someFunction, value) {
someFunction(value);
}
execute(function(word){ console.log(word) }, "Hello");
HTTP服务器
var http = require("http");
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}).listen(8888);
//或
var http = require("http");
function onRequest(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
路由
router.js
function route(pathname) {
console.log("About to route a request for " + pathname);
}
exports.route = route;
server.js
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;
index.js
var server = require("./server");
var router = require("./router");
server.start(router.route);