node.js|【Node.js】面试常考问题


Node问题梳理总结

      • 1、koa和express有哪些不同?
      • 2、两个Node.js进程如何通信?
      • 3、浏览器与Node中的事件循环有什么区别?
      • 4、如何实现JWT鉴权机制?说说思路
      • 5、Node性能如何进行监控以及优化?
      • 6、说说对中间件概念的理解,如何封装node中间件?
      • 7、Node文件查找的优先级以及require方法的文件查找策略
      • 8、Node中stream的理解?应用场景
      • 9、Node中的Buffer的理解?场景
      • 10、Node中process的理解,常用方法?
      • 11、Node有哪些全局对象
      • 12、对node的理解,优缺点,应用场景
      • 13、common.js和es6模块中引入的区别
      • 14、node单线程还是多线程
      • 15、node单线程为什么还能支持高并发
      • 16、node如何创建子进程

1、koa和express有哪些不同?
express框架是一个基于 Node.js 平台的极简、灵活的 web 应用开发框架,主要基于 Connect 中间件,并且自身封装了路由、视图处理等功能。
koa是 Express 原班人马基于ES6 新特性重新开发的框架,主要基于 co 中间件,框架自身不包含任何中间件,很多功能需要借助第三方中间件解决,但是由于其基于 ES6 generator 特性的异步流程控制,解决了 “callback hell” 和麻烦的错误处理问题。
相同点:两个框架都对http进行了封装。相关的api都差不多,同一批人所写。
不同点:
  • express内置了许多中间件可供使用,而koa没有。
  • express包含路由,视图渲染等特性,而koa只有http模块。
  • express的中间件模型为线型,而koa的中间件模型为U型,也可称为洋葱模型构造中间件。
  • express通过回调实现异步函数,在多个回调、多个中间件中写起来容易逻辑混乱。
总结
Express
优点:线性逻辑,通过中间件形式把业务逻辑细分、简化,一个请求进来经过一系列中间件处理后再响应给用户,清晰明了。
缺点:基于 callback 组合业务逻辑,业务逻辑复杂时嵌套过多,异常捕获困难。
Koa
优点:首先,借助 co 和 generator,很好地解决了异步流程控制和异常捕获问题。其次,Koa 把 Express 中内置的 router、view 等功能都移除了,使得框架本身更轻量。
缺点:社区相对较小
2、两个Node.js进程如何通信?
这里要分两种场景:
  • 不同电脑上的两个 Node.js 进程间通信
  • 同一台电脑上两个 Node.js 进程间通信
对于第一种场景,通常使用 TCP 或 HTTP 进行通信,而对于第二种场景,又分为两种子场景:
  • Node.js 进程和自己创建的 Node.js 子进程通信
  • Node.js 进程和另外不相关的 Node.js 进程通信
【node.js|【Node.js】面试常考问题】前者可以使用内置的 IPC 通信通道,后者可以使用自定义管道,接下来进行详细介绍:
  • 不同电脑上的两个Node.js进程通信:
首先得搞清楚如何标识网络中的进程? 网络层的 ip 地址可以唯一标识网络中的主机,而传输层的协议和端口可以唯一标识主机中的应用程序(进程),这样利用三元组(ip 地址,协议,端口)就可以标识网络的进程了
  • 同一台电脑上两个 Node.js 进程间通信
虽然网络 socket 也可用于同一台主机的进程间通讯(通过 loopback 地址 127.0.0.1),但是这种方式需要经过网络协议栈、需要打包拆包、计算校验和、维护序号和应答等,就是为网络通讯设计的,而同一台电脑上的两个进程可以有更高效的通信方式,即 IPC(Inter-Process Communication),在 unix 上具体的实现方式为 unix domain socket,这是服务器端和客户端之间通过本地打开的套接字文件进行通信的一种方法,与 TCP 通信不同,通信时指定本地文件,因此不进行域解析和外部通信,所以比 TCP 快,在同一台主机的传输速度是 TCP 的两倍。
使用自定义管道
如果是两个独立的 Node.js 进程,如何建立通信通道呢?在 Windows 上可以使用命名管道(Named PIPE),在 unix 上可以使用 unix domain socket,也是一个作为 server,另外一个作为 client,其中 server.js 代码如下:
3、浏览器与Node中的事件循环有什么区别?
浏览器:
执行栈,异步任务队列,宏任务、微任务:
  • 执行一只task(宏任务)
  • 执行完micro-task队列 (微任务)
常见的 task(宏任务) 比如:setTimeout、setInterval、script(整体代码)、 I/O 操作、UI 渲染等。 Ajax,DOM事件
常见的 micro-task 比如: new Promise().then(回调)、MutationObserver(html5新特性) 等。
  • 宏任务在DOM渲染后触发,如setTimeout
    微任务在DOM渲染前触发,如promise
宏任务先执行微任务再执行,
为什么很多人有微任务先执行?
script标签它就是宏任务 所以先执行的宏任务。在宏任务和微任务同级的情况下先执行微任务
在下面的代码中,微任务先输出
setTimeout(() => { console.log("宏任务"); }, 0); Promise.resolve().then((e) => { console.log("微任务"); });

先说一下事件循环:先执行宏任务,再检查有没有微任务,如果有就执行微任务,然后渲染dom,最后进入下一次事件循环
因为上面的代码会先输出微任务,再输出宏任务,
这是因为宏任务是下一个事件循环开始的宏任务,而微任务是这次事件循环就执行的微任务,他们之间隔了一个dom渲染。所以造成了微任务比宏任务更快执行的错觉。
Node:
Node的事件循环是libuv实现的
Node 10以前:
执行完一个阶段的所有任务
执行完nextTick队列里面的内容
然后执行完微任务队列的内容
Node 11以后: 和浏览器的行为统一了,都是每执行一个宏任务就执行完微任务队列。
还存在process.nextTick,其不属于事件循环的任何一个阶段,它属于该阶段与下阶段之间的过渡, 即本阶段执行结束, 进入下一个阶段前, 所要执行的回调,类似插队
4、如何实现JWT鉴权机制?说说思路
JWT(JSON Web Token),本质就是一个字符串书写规范,如下图,作用是用来在用户和服务器之间传递安全可靠的信息。
在目前前后端分离的开发过程中,使用token鉴权机制用于身份验证是最常见的方案,流程如下:
服务器当验证用户账号和密码正确的时候,给用户颁发一个令牌,这个令牌作为后续用户访问一些接口的凭证后续访问会根据这个令牌判断用户时候有权限进行访问
Token,分成了三部分,头部(Header)、载荷(Payload)、签名(Signature),并以.进行拼接。其中头部和载荷都是以JSON格式存放数据,只是进行了编码。
Token的使用分成了两部分:
生成token:登录成功的时候,颁发token
验证token:访问某些资源或者接口时,验证token
借助第三方库jsonwebtoken,通过jsonwebtoken 的 sign 方法生成一个 token:
在前端接收到token后,一般情况会通过localStorage进行缓存,然后将token放到HTTP 请求头Authorization 中,关于Authorization 的设置,前面要加上 Bearer ,注意后面带有空格
使用 koa-jwt 中间件进行验证,方式比较简单
5、Node性能如何进行监控以及优化?
6、说说对中间件概念的理解,如何封装node中间件?
在NodeJS中,中间件主要是指封装http请求细节处理的方法
例如在express、koa等web框架中,中间件的本质为一个回调函数,参数包含请求对象、响应对象和执行下一个中间件的函数,在这些中间件函数中,我们可以执行业务逻辑代码,修改请求和响应对象、返回响应数据等操作
下面则通过中间件封装http请求过程中几个常用的功能:
token校验、通过将公共逻辑的处理编写在中间件中,可以不用在每一个接口回调中做相同的代码编写,减少了冗杂代码,过程就如装饰者模式
7、Node文件查找的优先级以及require方法的文件查找策略
通过上面模块的文件查找策略之后,总结下文件查找的优先级:
  • 缓存的模块优先级最高
  • 如果是内置模块,则直接返回,优先级仅次缓存的模块
  • 如果是绝对路径 / 开头,则从根目录找
  • 如果是相对路径 ./开头,则从当前require文件相对位置找
  • 如果文件没有携带后缀,先从js、json、node按顺序查找
  • 如果是目录,则根据 package.json的main属性值决定目录下入口文件,默认情况为 index.js
  • 如果文件为第三方模块,则会引入 node_modules 文件,如果不在当前仓库文件中,则自动从上级递归查找,直到根目录
8、Node中stream的理解?应用场景
流(Stream),是一个数据传输手段,是端到端信息交换的一种方式,而且是有顺序的,是逐块读取数据、处理内容,用于顺序读取输入或写入输出
stream的应用场景主要就是处理IO操作,而http请求和文件操作都属于IO操作
9、Node中的Buffer的理解?场景
在Node应用中,需要处理网络协议、操作数据库、处理图片、接收上传文件等,在网络流和文件的操作中,要处理大量二进制数据,而Buffer就是在内存中开辟一片区域(初次初始化为8KB),用来存放二进制数据
这里的等待区就指的缓冲区(Buffer),它是计算机中的一个小物理单位,通常位于计算机的 RAM 中
10、Node中process的理解,常用方法?
process 对象是一个全局变量,提供了有关当前 Node.js 进程的信息并对其进行控制,作为一个全局变量
我们都知道,进程计算机系统进行资源分配和调度的基本单位,是操作系统结构的基础,是线程的容器
由于JavaScript是一个单线程语言,所以通过node xxx启动一个文件后,只有一条主线程
process.nextTick()
我们知道NodeJs是基于事件轮询,在这个过程中,同一时间只会处理一件事情
在这种处理模式下,process.nextTick()就是定义出一个动作,并且让这个动作在下一个事件轮询的时间点上执行
两者区别在于:
process.nextTick()会在这一次event loop的call stack清空后(下一次event loop开始前)再调用callback
setTimeout()是并不知道什么时候call stack清空的,所以何时调用callback函数是不确定的
11、Node有哪些全局对象
在浏览器 JavaScript 中,通常 window 是全局对象, 而 Nodejs 中的全局对象是 global
在NodeJS里,是不可能在最外层定义一个变量,因为所有的用户代码都是当前模块的,只在当前模块里可用,但可以通过exports对象的使用将其传递给模块外部
所以,在NodeJS中,用var声明的变量并不属于全局的变量,只在当前模块生效
像上述的global全局对象则在全局作用域中,任何全局变量、函数、对象都是该对象的一个属性值
12、对node的理解,优缺点,应用场景
Node.js 是一个开源与跨平台的 JavaScript 运行时环境
在浏览器外运行 V8 JavaScript 引擎(Google Chrome 的内核),利用事件驱动、非阻塞和异步输入输出模型等技术提高性能
可以理解为 Node.js 就是一个服务器端的、非阻塞式I/O的、事件驱动的JavaScript运行环境
二、优缺点
优点:
处理高并发场景性能更佳
适合I/O密集型应用,值的是应用在运行极限时,CPU占用率仍然比较低,大部分时间是在做 I/O硬盘内存读写操作
因为Nodejs是单线程,带来的缺点有:
不适合CPU密集型应用
只支持单核CPU,不能充分利用CPU
可靠性低,一旦代码某个环节崩溃,整个系统都崩溃
具体场景可以表现为如下:
第一大类:用户表单收集系统、后台管理系统、实时交互系统、考试系统、联网软件、高并发量的web应用程序
第二大类:基于web、canvas等多人联网游戏
第三大类:基于web的多人实时聊天客户端、聊天室、图文直播
第四大类:单页面浏览器应用程序
第五大类:操作数据库、为前端和移动端提供基于json的API
13、common.js和es6模块中引入的区别
Common]S是一种模块规范,最初被应用于Nodejs,成为Nodejs 的模块规范。
运行在浏览器端的JavaScript由于也缺少类似的规范,在ES6出来之前,前端也实现了一套相同的模块规范(例如: AMD),用来对前端模块进行管理。
自ES6起,引入了一套新的ES6 Module规范,在语言标准的层面上实现了模块功能,而且实现得相当简单,有望成为浏览器和服务器通用的模块解决方案。
在使用上的差别主要有:
CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用CommonJS模块是运行时加载,ES6模块是编译时输出接口
CommonJs是单个值导出,ES6 Module可以导出多个
CommonJs是动态语法可以写在判断里,ES6 Module静态语法只能写在顶层CommonJs的this是当前模块,ES6 Module的this是undefined
14、node单线程还是多线程
15、node单线程为什么还能支持高并发
16、node如何创建子进程

    推荐阅读