JS高级--node.JS
大家需要知道nodejs 的出现,允许我们js开发人员使用 js 来开发服务器。
node.js 特点
-
它是一个 javascript 的运行环境
-
依赖于 V8 引擎进行代码解析
-
-
非阻塞 I/O
-
轻量、可伸缩
-
单线程:指的是 nodejs 在运行 js 代码的时候是单线程,并不是说整个 node.js 是单线程。
REPL 环境,英语全称 read-eval-print-loop,翻译成中文就称之为“即时运行环境”,在这个环境里面,可以运行 js 代码。
该环境的意义在于测试一些小段的代码。一般比较的成熟的语言都提供REPL环境。
模块化
如果想要开发大型应用,必须要有模块化。
最早,js 是没有模块话的概念的,因为最早 js 的定位就是脚本语言(玩具语言)。
但是随着 js 走向服务器端的开发,那么必然也是需要模块化。
总结一下模块化的好处:
-
生产效率高
-
维护成本低
接下来我们就需要看一下 node.js 中如何实现模块化,一般来讲,分为3类
-
文件模块
-
核心模块
-
第三方模块
引入模块统一使用 require 方法。
const readline = require('readline-sync');
require(“路径.扩展名”)
// 相对路径
require('./index.js');
require('../index.js');
// 绝对路径
require('/src/index.js'); // 一个斜杠 / 代表根目录
核心模块
核心模块是指 nodejs 内置的模块,比如 fs、os、http、path...
require('fs');
require('path');
第三方模块
require('readline-sync');
CommonJS 规范
之前,我们接触过一个东西,叫做 ECMAScript,它就是 js 语言的规范。
我们可以这么说,ECMAScript 是规范,JavaScript 是这个规范的一种实现,例如 adobe 的 flash 里面使用的 ActionScript 也是一种 ECMAScript 的实现。
ECMAScript 虽然说是 js 的规范,但是它只管客户端,Commonjs 管的就是浏览器端以外的环境。
CommonJS一些著名的实现,node.js、mongodb、couchdb、coffiejs。
CommonJS 中的模块导入导出
在 CommonJS 中,通过 module.exports 来导出一个模块,通过 require 来导入一个模块。
module.exports 可以将他想象成一个对象。
// index.js
// 导出模块
module.exports.name = 'xiejie';
module.exports.sayHello = function(){
console.log('Hello');
}
// index2.js
// 导入模块
let obj = require('./index.js');
console.log(obj.name); // xiejie
obj.sayHello(); // Hello
为了让开发人员使用起来更加方便,nodejs 还提供了一个 exports 的对象,它是指向 module.exports 的。
// index.js
// 导出模块
exports.name = 'xiejie';
exports.sayHello = function(){
console.log('Hello');
}
// index2.js
// 导入模块
let obj = require('./index.js');
console.log(obj.name); // xiejie
obj.sayHello(); // Hello
但是两者之间是有区别,上面我们在介绍 exports 的时候,我们有讲过, exports 是指向 module.exports 的,也就是说,最终,导出的是 module.exports
// index.js
module.exports = 'F71';
// index2.js
let obj = require('./index.js');
console.log(obj); // F71
// index.js
exports = 'F71';
// index2.js
let obj = require('./index.js');
console.log(obj); // {}
nodejs 的两大特点
nodejs 有两个最大的特点:异步无阻塞和事件驱动。
1. 异步无阻塞
传统的同步代码,如果在它的线程中遇到磁盘读写、发送请求,就会阻塞后面的代码。像 Java 这种语言,采用的是多线程的方式来解决这个问题。
js 采用的异步无阻塞的方式来解决这个问题,遇到异步代码,会交给异步模块。
下面是同步处理阻塞IO的示例:
const fs = require('fs');
console.log('开始写入文件');
// 如果要捕获错误,使用 try...catch
try {
// 书写尝试执行的代码
const content = fs.readFileSync('./test111.txt');
console.log(content.toString());
} catch (err) {
console.log(err);
}
console.log('结束写入文件');
异步的方式:
const fs = require('fs');
console.log('开始写入文件');
fs.readFile('./test.txt',function(err,data){
if(err) throw err;
console.log(data.toString());
})
console.log('结束写入文件');
通过上面的例子,我们可以看出,在 node中,由于采用异步的方式来处理阻塞代码,所以在回到函数中,采用错误优先原则。
-
事件驱动
这里其实就是两个知识点:EventEmitter,事件驱动模式,EventEmitter 是 node 里面为我们提供的一个模块,允许我们自定义事件。
之前我们学过事件,例如 click,mouseenter,mousemove 这些。
例如:
const EventEmitter = require('events').EventEmitter;
const event = new EventEmitter();
// 手动定义一个事件
event.on('F71',function(){
console.log('你触发了这个事件');
})
// 手动来触发
setTimeout(function(){
event.emit('F71');
},2000);
nodejs 系统架构
nodejs 的架构如下图:

该图展示了整个 Node 的运行原理,从左到右,从上到下,整个 Node 被分为了 4 层,分别是应用层、V8 引擎层、Node API层和LIBUV层。
应用层:即 JavaScript 交互层,常见的就是 Node 的模块,比如http,fs。
V8 引擎层:即利用 V8 引擎来解析 JavaScript 语法,进而和下层 API 交互。
NodeAPI 层:为上层模块提供系统调用,一般是由 C 语言来实现,和操作系统进行交互。
LIBUV 层:是跨平台的底层封装,实现了事件循环、文件操作等,是 Node 实现异步的核心。
无论是 Linux 平台还是 Windows 平台,Node 内部都是通过线程池来完成异步 I/O 操作的,而 LIBUV 针对不同平台的差异性实现了统一调用。
当我们使用 Node.js 的时候,会在 JavaScript 中触发一些命令调用方法,这些方法会被包装成一个对象,放入线程池,然后前面的方法就返回了,继续执行下面的 JavaScript 代码。
线程池中采用多线程的方式执行,执行完的对象放入事件循环队列。
事件循环队列采用类似 while(true) 这种循环的方式,不断的查看是否有事件,并且读取是否包含回调,由于前面回调函数被包装到对象中,这里直接调用执行就可以了。
因此,Node.js 的单线程仅仅是指 JavaScript 运行在单线程中,而并非整个 Node.js 运行环境是单线程。

浙公网安备 33010602011771号