Node.js是什么
原生js是不能脱离浏览器运行的,而Node.js就能够让js脱离浏览器运行在服务器上。
简而言之,Node.js就是运行在服务端的JavaScript。
Node是V8引擎的容器,Node.js是基于Google的V8引擎的。
nodejs是用C++开发的一种运行于服务器端的语言,node的底层是C++,是可以直接运行在电脑上的。
Node.js的作用
- 编译js代码
- 读、写电脑上的文件
- 连接数据库
- 充当Web服务器
回调函数
Node.js 异步编程的直接体现就是回调。异步编程依托于回调来实现,但不能说使用了回调后程序就异步化。
异步与回调的关系
-
关联
异步任务需要在得到结果时通知JS来读取结果。等待通知的过程就是异步,通知JS读取结果的过程就是回调。 -
区别
异步任务需要用到回调函数来通知结果;
回调函数却不一定存在于异步任务中,同步任务中也可以用到回调函数。
回调函数就是自己写了却不调用,给别人调用的函数。
EventEmitter
Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。
Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件,一个 fs.readStream 对象会在文件被打开的时候触发一个事件。
所有这些产生事件的对象都是 events.EventEmitter 的实例。
EventEmitter 类:
events 模块只提供了一个对象: events.EventEmitter。EventEmitter的核心就是事件触发与事件监听器功能的封装。
EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持若干个事件监听器。
当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。
继承EventEmitter:
大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
Buffer(缓冲区)
JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。
Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据。
创建Buffer类 | Buffer.alloc()_Buffer.from() |
写入缓冲区 | buf.write() |
从缓冲区读取数据 | buf.toString() |
将Buffer转换为JSON对象 | buf.toJSON() |
缓冲区合并 | Buffer.concat() |
缓冲区比较 | buf.compare() |
复制缓冲区 | buf.copy() |
剪裁缓冲区 | buf.slice() |
缓冲区长度 | buf.length |
Stream
Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。
四种流类型:
- Readable - 可读操作。
- Writable - 可写操作。
- Duplex - 可读可写操作。
- Transform - 操作被写入数据,然后读出结果。
常用的事件有:
- 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;
});
var fs = require("fs");
var data = '写入文件数据内容';
// 创建一个可以写入的流
var writerStream = fs.createWriteStream('output.txt');
// 使用 utf8 编码写入数据
writerStream.write(data,'UTF8');
// 标记文件末尾
writerStream.end();
管道流
管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。
var fs = require("fs");
// 创建一个可读流
var readerStream = fs.createReadStream('input.txt');
// 创建一个写入流
var writerStream = fs.createWriteStream('output.txt');
// 管道读写操作
readerStream.pipe(writerStream);
链式流
链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。
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'));
// 解压 input.txt.gz 文件为 input.txt
fs.createReadStream('input.txt.gz')
.pipe(zlib.createGunzip())
.pipe(fs.createWriteStream('input.txt'));
模块系统
- 模块是Node.js 应用程序的基本组成部分。
- 文件和模块是一一对应的,一个 Node.js 文件就是一个模块,这个文件可以是JavaScript 代码、JSON。
Node.js 提供了exports和require两个对象,其中exports是模块公开的接口,require用于从外部获取一个模块的接口,即所获取模块的 exports 对象。
引入当前目录的模块:
var helloWorld= require('./helloWorld');
hello.js 通过 exports 对象把 world 作为模块的访问接口,通过 require(‘./hello’)加载这个模块后,就可以直接访问hello.js 中exports对象的成员函数。
//hello.js
exports.world = () => {
console.log("hello world!");
this.saySomething = () =>{
console.log("hello world222222");
}
this.saySomething();
}
//main.js
const hello = require('./hello');
hello.world();
使用module.exports = Hello代替exports.world = function(){},在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports。
//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('Wocccccc');
hello.sayHello();
把一个对象封装到模块中:
//hello.js
module.exports = () => {
console.log("hello world!");
this.saySomething = "hhhhhhhha";
this.world = () =>{
console.log("??????");
}
}
//main.js
var Hello = require('./hello');
hello = new Hello();
console.log(hello.saySomething);
hello.world();
Node.js 中存在 4 类模块(原生模块和3种文件模块):
- http、fs、path等,原生模块。
- ./mod或…/mod,相对路径的文件模块。
- /pathtomodule/mod,绝对路径的文件模块。
- mod,非原生模块的文件模块。
注:
-exports 和 module.exports 的使用:
如果要对外暴露属性或方法,就用 exports 就行,要暴露对象就用 module.exports;
-若同时使用 exports 和 module.exports:
如果先使用 exports 对外暴露属性或方法,再使用 module.exports 暴露对象,会使得 exports 上暴露的属性或者方法失效。
函数
一个函数可以作为另一个函数的参数。我们可以先定义一个函数,然后传递;
function say(word) {
console.log(word);
}
function execute(someFunction, value) {
someFunction(value);
}
execute(say, "Hello");
也可以在传递参数的地方直接定义函数。
//匿名函数
const hello = (say, value)=>{
say(value);
};
hello((word)=>{console.log(word);}, "hello");
全局对象
Node.js中的全局对象是global,所有全局变量(global自身除外)都是 global对象的属性。
global最根本的作用是作为全局变量的宿主。当你定义一个全局变量时,这个变量同时也会成为全局对象的属性。
最好不要使用var定义变量以避免引入全局变量,因为全局变量会污染命名空间,提高代码的耦合风险。
__filename 表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径0。 |
__dirname 表示当前执行脚本所在的目录。 |
setTimeout(function, ms) 全局函数在指定的毫秒数后执行函数function。setTimeout()只执行一次指定函数。 |
clearTimeout(T) 全局函数用于停止一个之前通过 setTimeout()创建的定时器 T。 |
setInterval(function, ms) 全局函数在指定的毫秒(ms)数后执行函数function。 |
clearInterval(T) 函数来清除定时器,setInterval()方法会不停地调用函数,直到 clearInterval()被调用或窗口被关闭。 |
console 用于提供控制台标准输出。 |
process 是一个全局变量,即global对象的属性。 |
- util.callbackify(original)
- util.inherits(constructor, superConstructor)
- util.inspect(object,[showHidden],[depth],[colors])
- util.isArray(object)
- util.isRegExp(object)
- util.isDate(object)
文件系统
Node导入文件系统模块:
var fs = require("fs")
同步异步
Node.js 文件系统模块中的方法均有异步和同步版本。
异步的方法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error)。
异步方法比起同步,异步方法性能更高,速度更快,而且没有阻塞。
var fs = require("fs");
// 异步读取
fs.readFile('input.txt', function (err, data) {
if (err) {
return console.error(err);
}
console.log("异步读取: " + data.toString());
});
// 同步读取
var data = fs.readFileSync('input.txt');
console.log("同步读取: " + data.toString());
console.log("程序执行完毕。");
异步模式下打开文件:
fs.open(path, flags[, mode], callback) //callback(err, fd)
异步模式下获取文件信息:
fs.stat(path, callback) //callback(err, stats), stats 是fs.Stats对象
异步模式下写入文件:
fs.writeFile(file, data[, options], callback) //options(encoding, mode, flag),callback(err)
异步模式下读取文件:
fs.read(fd, buffer, offset, length, position, callback) //callback(err, bytesRead, buffer)
异步模式下关闭文件:
fs.close(fd, callback) //callback没有参数
异步模式下截取文件:
fs.ftruncate(fd, len, callback) //callback没有参数
删除文件:
fs.unlink(path, callback) //callback没有参数
创建目录:
fs.mkdir(path[, options], callback) //options(recursive, mode),callback没有参数
读取目录:
fs.readdir(path, callback) //callback(err, files)
删除目录:
fs.rmdir(path, callback) //callback没有参数
常用工具模块
os模块提供了一些基本的系统操作函数
path模块提供了一些用于处理文件路径的小工具
Net模块提供了一些用于底层的网络通信的小工具
DNS模块用于解析域名
Domain简化异步代码的异常处理,可以捕捉处理try catch无法捕捉的异常。
多线程
Node.js 是以单线程的模式运行的;但它使用的是事件驱动来处理并发,我们可以在多核 cpu 的系统上创建多个子进程。
每个子进程总是带有三个流对象:child.stdin, child.stdout 和child.stderr。
Node提供child_process模块来创建子进程:
child_process.exec(command[, options], callback(err, stdout, stderr))
使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回。exec()方法返回最大的缓冲区,并等待进程结束,一次性返回缓冲区的内容。child_process.spawn(command[, args][, options])
使用指定的命令行参数创建新进程。spawn()方法返回流 (stdout & stderr),在进程返回大量数据时使用。进程一旦开始执行时spawn()就开始接收响应。child_process.fork(modulePath[, args][, options])
spawn()方法的特殊形式,用于创建进程。