Node面试题汇总

1、面试官:说说你对Node.js 的理解?

Node.js 是一个开源与跨平台的 JavaScript 运行时环境
在浏览器外运行 V8 JavaScript 引擎(Google Chrome 的内核),利用事件驱动、非阻塞和异步输入输出模型等技术提高性能
可以理解为 Node.js 就是一个服务器端的、非阻塞式I/O的、事件驱动的JavaScript运行环境
非阻塞异步
Nodejs采用了非阻塞型I/O机制,在做I/O操作的时候不会造成任何的阻塞,当完成之后,以时间的形式通知执行操作
例如在执行了访问数据库的代码之后,将立即转而执行其后面的代码,把数据库返回结果的处理代码放在回调函数中,从而提高了程序的执行效率
事件驱动
事件驱动就是当进来一个新的请求的时,请求将会被压入一个事件队列中,然后通过一个循环来检测队列中的事件状态变化,如果检测到有状态变化的事件,那么就执行该事件对应的处理代码,一般都是回调函数
比如读取一个文件,文件读取完毕后,就会触发对应的状态,然后通过对应的回调函数来进行处理
优点:
1、处理高并发场景性能更佳
2、适合I/O密集型应用,值的是应用在运行极限时,CPU占用率仍然比较低,大部分时间是在做 I/O硬盘内存读写操作
因为Nodejs是单线程,带来的缺点有:
1、不适合CPU密集型应用
2、只支持单核CPU,不能充分利用CPU
3、可靠性低,一旦代码某个环节崩溃,整个系统都崩溃

2、面试官:说说 Node. js 有哪些全局对象?

在浏览器 JavaScript 中,通常window 是全局对象, 而 Nodejs中的全局对象是 global
模块级别的全局对象
这些全局对象是模块中的变量,只是每个模块都有,看起来就像全局变量,像在命令交互中是不可以使用,包括:
__dirname __filename exports module require

  • _dirname:获取当前文件所在的路径,不包括后面的文件名
  • _filename:获取当前文件所在的路径和文件名称,包括后面的文件名称
  • exports:module.exports 用于指定一个模块所导出的内容,即可以通过 require() 访问的内容
  • module:对当前模块的引用,通过module.exports 用于指定一个模块所导出的内容,即可以通过 require() 访问的内容
  • require 用于引入模块、 JSON、或本地文件。 可以从 node_modules 引入模块。

可以使用相对路径引入本地模块或JSON文件,路径会根据__dirname定义的目录名或当前工作目录进行处理

3、面试官:说说对 Node 中的 process 的理解?

process 对象是一个全局变量,提供了有关当前 Node.js进程的信息并对其进行控制,作为一个全局变量

4、面试官:说说对 Node 中的 fs模块的理解?

fs(filesystem),该模块提供本地文件的读写能力,基本上是POSIX文件操作命令的简单包装
可以说,所有与文件的操作都是通过fs核心模块实现
导入模块如下:

const fs = require('fs');

这个模块对所有文件系统操作提供异步(不具有sync 后缀)和同步(具有 sync 后缀)两种操作方式,而供开发者选择

5、面试官:说说对 Node 中的 Buffer 的理解?

在Node应用中,需要处理网络协议、操作数据库、处理图片、接收上传文件等,在网络流和文件的操作中,要处理大量二进制数据,而Buffer(缓冲区)就是在内存中开辟一片区域(初次初始化为8KB),用来存放二进制数据
简单来讲,Nodejs不能控制数据传输的速度和到达时间,只能决定何时发送数据,如果还没到发送时间,则将数据放在Buffer中,即在RAM中,直至将它们发送完毕
Buffer是用来存储二进制数据,其的形式可以理解成一个数组,数组中的每一项,都可以保存8位二进制:00000000,也就是一个字节

6、面试官:说说对 Node 中的 Stream 的理解?应用场景?

流(Stream),是一个数据传输手段,是端到端信息交换的一种方式,而且是有顺序的,是逐块读取数据、处理内容,用于顺序读取输入或写入输出
Node.js中很多对象都实现了流,总之它是会冒数据(以 Buffer 为单位)
它的独特之处在于,它不像传统的程序那样一次将一个文件读入内存,而是逐块读取数据、处理其内容,而不是将其全部保存在内存中
流可以分成三部分:source、dest、pipe
在source和dest之间有一个连接的管道pipe,它的基本语法是source.pipe(dest),source和dest就是通过pipe连接,让数据从source流向了dest
在NodeJS,几乎所有的地方都使用到了流的概念,分成四个种类:

  • 可写流:可写入数据的流。例如 fs.createWriteStream() 可以使用流将数据写入文件
  • 可读流: 可读取数据的流。例如fs.createReadStream() 可以从文件读取内容
  • 双工流: 既可读又可写的流。例如 net.Socket
  • 转换流: 可以在数据写入和读取时修改或转换数据的流。例如,在文件压缩操作中,可以向文件写入压缩数据,并从文件中读取解压数据

7、面试官:说说Node中的EventEmitter? 如何实现一个EventEmitter?

Node采用了事件驱动机制,而EventEmitter就是Node实现事件驱动的基础
在EventEmitter的基础上,Node几乎所有的模块都继承了这个类,这些模块拥有了自己的事件,可以绑定/触发监听器,实现了异步操作
这些产生事件的对象都是 events.EventEmitter 的实例,这些对象有一个 eventEmitter.on() 函数,用于将一个或多个函数绑定到命名事件上

8、面试官:说说对Nodejs中的事件循环机制理解?

js可以在浏览器中执行又可以在node中执行,但是它们的事件循环机制并不是一样的。并且有很大的区别。在Node中的事件循环分为六个阶段。
按顺序进行:
1、Timers:用于存储定时器的回调函数(setlnterval,setTimeout)。
2、Pending callbacks:执行与操作系统相关的回调函数,比如启动服务器端应用时监听端口操作的回调函数就在这里调用。
3、idle,prepare:系统内部使用。(这个我们程序员不用管)
4、Poll:存储1/O操作的回调函数队列,比如文件读写操作的回调函数。
5、Check:存储setlmmediate的回调函数。
setlmmediate方法用来把一些需要长时间运行的操作放在一个回调函数里,在浏览器完成后面的其他语句后,就立刻执行这个回调函数。
6、Closingcallbacks:执行与关闭事件相关的回调,例如关闭数据库连接的回调函数等。
总结:
1、当主线程同步代码执行完毕后才会进入事件循环
2、事件循环总共分六个阶段
3、事件循环中会先执行微任务再执行宏任务。
4、微任务会穿插在这六个阶段之间执行,每进入到下个阶段前会清空当前的微任务队列。
5、微任务中process.nextTick的优先级最高,会优先执行。
宏任务
Setlnterval、setimeout、setlmmediate、I/O
微任务
Promise.then、Promise.catch、Promise.finally、process.nextTick

9、面试官:说说 Node 文件查找的优先级以及 Require 方法的文件查找策略?

通过require函数导入其他模块(自定义模块、系统模块、第三方库模块)中的内容,require参数较为简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同
1、缓存的模块优先级最高
2、如果是内置模块,则直接返回,优先级仅次缓存的模块
3、如果是绝对路径 / 开头,则从根目录找
4、如果是相对路径 ./开头,则从当前require文件相对位置找
5、如果文件没有携带后缀,先从js、json、node按顺序查找
6、如果是目录,则根据 package.json的main属性值决定目录下入口文件,默认情况为 index.js
7、如果文件为第三方模块,则会引入 node_modules 文件,如果不在当前仓库文件中,则自动从上级递归查找,直到根目录

10、面试官:如何实现jwt鉴权机制?说说你的思路

JWT(JSON Web Token),本质就是一个字符串书写规范,作用是用来在用户和服务器之间传递安全可靠的信息,使用token鉴权机制用于身份验证是最常见的方案
Token,分成了三部分,头部(Header)、载荷(Payload)、签名(Signature),并以.进行拼接。

  • 载荷即消息体,这里会存放实际的内容,也就是Token的数据声明,例如用户的id和name,默认情况下也会携带令牌的签发时间iat,通过还可以设置过期时间
  • 主要说一下签名,签名是对头部和载荷内容进行签名,一般情况,设置一个密钥,对前两个的结果进行HMACSHA25算法,这样一旦前面两部分数据被篡改,只要服务器加密用的密钥没有泄露,得到的签名肯定和之前的签名不一致

Token的使用分成了两部分:
1、生成token:登录成功的时候,颁发token
2、验证token:访问某些资源或者接口时,验证token
可以借助第三方库jsonwebtoken,通过jsonwebtoken 的 sign 方法生成一个 token
之后前端可以通过路由进行校验等等

11、面试官:如何实现文件上传?说说你的思路

对于文件上传,我们需要设置请求头为content-type:multipart/form-data
Multipart为互联网上的混合资源,就是资源由多种元素组成,form-data表示可以使用HTML Forms 和 POST 方法上传文件
文件上传步骤:
1、 action里确定提交的接口,enctype=“multipart/form-data” 就是指定上传文件格式,input 的 name 属性一定要等于file
2、 之后在服务器中,采用koa2中间件的形式解析上传的文件数据
首先获取上传的文件,获取文件数据后,可以通过fs模块将文件保存到指定目录,

12、面试官:如果让你来设计一个分页功能, 你会怎么设计? 前后端如何交互?

前端实现分页功能,需要后端返回必要的数据,如总的页数,总的数据量,当前页,当前的数据,后端采用mysql作为数据的持久性存储
前端向后端发送目标的页码page以及每页显示数据的数量pageSize,
默认情况每次取n条数据,则每一条数据的起始位置start为:

const start = (page - 1) * pageSize

数据库中进行截取从start到start+pageSize之间(左闭右开)的数据,返回给前端

13、面试官:Node性能如何进行监控以及优化?

Node作为一门服务端语言,性能方面尤为重要,其衡量指标一般有如下:
1、 CPU 2、内存 3、I/O 4、网络
CPU主要分成了两部分:
1、CPU负载:在某个时间段内,占用以及等待CPU的进程总数
2、CPU使用率:CPU时间占用状况,等于 1 - 空闲CPU时间(idle time) / CPU总时间
这两个指标都是用来评估系统当前CPU的繁忙程度的量化指标
Node应用一般不会消耗很多的CPU,如果CPU占用率高,则表明应用存在很多同步操作,导致异步任务回调被阻塞
内存指标
内存是一个非常容易量化的指标。 内存占用率是评判一个系统的内存瓶颈的常见指标
在Node中,一个进程的最大内存容量为1.5GB。因此我们需要减少内存泄露
磁盘 I/O
硬盘的IO 开销是非常昂贵的,硬盘 IO 花费的 CPU 时钟周期是内存的 164000 倍,内存 IO比磁盘IO 快非常多,所以使用内存缓存数据是有效的优化方法。常用的工具如 redis、memcached等
性能方面的监控,一般情况都需要借助工具来实现
如Easy-Monitor 2.0,其是轻量级的 Node.js 项目内核性能监控 + 分析工具,在默认模式下,只需要在项目入口文件 require 一次,无需改动任何业务代码即可开启内核级别的性能监控分析,查看进程界面
优化方式:
1、使用最新版本Node.js
每个版本的性能提升主要来自于两个方面:
V8 的版本更新
Node.js 内部代码的更新优化
2、正确使用流 Stream
在Node中,很多对象都实现了流,对于一个大文件可以通过流的形式发送,不需要将其完全读入内存
3、代码层面优化
合并查询,将多次查询合并一次,减少数据库的查询次数
4、通过减少内存占用,可以提高服务器的性能
在 V8 中,主要将内存分为新生代和老生代两代:

  • 新生代:对象的存活时间较短。新生对象或只经过一次垃圾回收的对象
  • 老生代:对象存活时间较长。经历过一次或多次垃圾回收的对象

若新生代内存空间不够,直接分配到老生代
而节省内存最好的方式是使用池,其将频用、可复用对象存储起来,减少创建和销毁操作
使用对象池的机制,对这种频繁需要创建和销毁的对象保存在一个对象池中。每次用到该对象时,就取对象池空闲的对象,并对它进行初始化操作,从而提高框架的性能

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值