>  기사  >  웹 프론트엔드  >  Nodejs 인터뷰 질문에 답할 수 있나요?

Nodejs 인터뷰 질문에 답할 수 있나요?

青灯夜游
青灯夜游앞으로
2021-07-12 17:32:411795검색

이 기사에서는 Nodejs모듈 메커니즘, 비동기 I/O, V8 가비지 수집 메커니즘, 버퍼 모듈, webSocket, 프로세스 통신, 미들웨어 등에 대한 몇 가지 인터뷰 질문을 공유할 것입니다. 답변해 주실 수 있나요? 몇 개나 정답을 맞힐 수 있나요? 아래에서 함께 시도해 보세요!

Nodejs 인터뷰 질문에 답할 수 있나요?

1. 노드 모듈 메커니즘

1.1 노드의 모듈이 무엇인지 소개해주세요

노드에서 각 파일 모듈은 객체이며 그 정의는 다음과 같습니다.

function Module(id, parent) {
  this.id = id;
  this.exports = {};
  this.parent = parent;
  this.filename = null;
  this.loaded = false;
  this.children = [];
}

module.exports = Module;

var module = new Module(filename, parent);

모든 모듈은 모듈의 인스턴스. 보시다시피 현재 모듈(module.js)도 Module의 인스턴스입니다.

[추천 학습: "nodejs Tutorial"]

1.2 require의 모듈 로딩 메커니즘을 소개해주세요

이 질문은 기본적으로 면접관의 Node 모듈 메커니즘에 대한 이해를 이해할 수 있습니다. 기본적으로 인터뷰에서 언급한

  • 1. 모듈 경로를 먼저 계산하세요
  • 2. 모듈이 캐시에 있으면 캐시를 꺼내세요
  • 3. 모듈의 내보내기 속성만 출력하세요.
  • // require 其实内部调用 Module._load 方法
    Module._load = function(request, parent, isMain) {
      //  计算绝对路径
      var filename = Module._resolveFilename(request, parent);
    
      //  第一步:如果有缓存,取出缓存
      var cachedModule = Module._cache[filename];
      if (cachedModule) {
        return cachedModule.exports;
    
      // 第二步:是否为内置模块
      if (NativeModule.exists(filename)) {
        return NativeModule.require(filename);
      }
      
      /********************************这里注意了**************************/
      // 第三步:生成模块实例,存入缓存
      // 这里的Module就是我们上面的1.1定义的Module
      var module = new Module(filename, parent);
      Module._cache[filename] = module;
    
      /********************************这里注意了**************************/
      // 第四步:加载模块
      // 下面的module.load实际上是Module原型上有一个方法叫Module.prototype.load
      try {
        module.load(filename);
        hadException = false;
      } finally {
        if (hadException) {
          delete Module._cache[filename];
        }
      }
    
      // 第五步:输出模块的exports属性
      return module.exports;
    };
  • 그럼 계속 다음 질문으로 넘어가겠습니다

1.3 모듈을 로드할 때 각 모듈에 __dirname 및 __filename 속성이 있는 이유는 무엇입니까? 새 모듈을 만들 때 1.1부에는 이 두 속성이 없다는 것을 알 수 있습니다. 이 두 속성은

// 上面(1.2部分)的第四步module.load(filename)
// 这一步,module模块相当于被包装了,包装形式如下
// 加载js模块,相当于下面的代码(加载node模块和json模块逻辑不一样)
(function (exports, require, module, __filename, __dirname) {
  // 模块源码
  // 假如模块代码如下
  var math = require('math');
  exports.area = function(radius){
      return Math.PI * radius * radius
  }
});
즉, 각 모듈은 __filename 및 __dirname 매개 변수를 전달합니다. 이 두 매개 변수는 모듈 자체에 없지만 외부 세계에서 전달됩니다.

1.4 두 가지 노드 내보내기 모듈이 있습니다. 하나는 imports.xxx=xxx이고 Module.exports={}입니다.

exports는 실제로 module.exports
  • 입니다. 1.3 문제는 이미 문제를 설명했습니다. 그러면 위대한 스승 Liao Xuefeng의 설명을 인용하겠습니다. 더 명확하게 설명할 수 있기를 바랍니다
  • module.exports vs exports
    很多时候,你会看到,在Node环境中,有两种方法可以在一个模块中输出变量:
    
    方法一:对module.exports赋值:
    
    // hello.js
    
    function hello() {
        console.log('Hello, world!');
    }
    
    function greet(name) {
        console.log('Hello, ' + name + '!');
    }
    
    module.exports = {
        hello: hello,
        greet: greet
    };
    方法二:直接使用exports:
    
    // hello.js
    
    function hello() {
        console.log('Hello, world!');
    }
    
    function greet(name) {
        console.log('Hello, ' + name + '!');
    }
    
    function hello() {
        console.log('Hello, world!');
    }
    
    exports.hello = hello;
    exports.greet = greet;
    但是你不可以直接对exports赋值:
    
    // 代码可以执行,但是模块并没有输出任何变量:
    exports = {
        hello: hello,
        greet: greet
    };
    如果你对上面的写法感到十分困惑,不要着急,我们来分析Node的加载机制:
    
    首先,Node会把整个待加载的hello.js文件放入一个包装函数load中执行。在执行这个load()函数前,Node准备好了module变量:
    
    var module = {
        id: 'hello',
        exports: {}
    };
    load()函数最终返回module.exports:
    
    var load = function (exports, module) {
        // hello.js的文件内容
        ...
        // load函数返回:
        return module.exports;
    };
    
    var exportes = load(module.exports, module);
    也就是说,默认情况下,Node准备的exports变量和module.exports变量实际上是同一个变量,并且初始化为空对象{},于是,我们可以写:
    
    exports.foo = function () { return 'foo'; };
    exports.bar = function () { return 'bar'; };
    也可以写:
    
    module.exports.foo = function () { return 'foo'; };
    module.exports.bar = function () { return 'bar'; };
    换句话说,Node默认给你准备了一个空对象{},这样你可以直接往里面加东西。
    
    但是,如果我们要输出的是一个函数或数组,那么,只能给module.exports赋值:
    
    module.exports = function () { return 'foo'; };
    给exports赋值是无效的,因为赋值后,module.exports仍然是空对象{}。
    
    结论
    如果要输出一个键值对象{},可以利用exports这个已存在的空对象{},并继续在上面添加新的键值;
    
    如果要输出一个函数或数组,必须直接对module.exports对象赋值。
    
    所以我们可以得出结论:直接对module.exports赋值,可以应对任何情况:
    
    module.exports = {
        foo: function () { return 'foo'; }
    };
    或者:
    
    module.exports = function () { return 'foo'; };
    最终,我们强烈建议使用module.exports = xxx的方式来输出模块变量,这样,你只需要记忆一种方法。
2. Node의 비동기 I/O

대부분의 답변입니다. 이 장의 질문은 Pu Ling의 "NodeJS in a Simple Way"에서 빌려왔습니다

2.1 Node 이벤트 루프를 소개해주세요. 프로세스

    프로세스가 시작되면 Node는 while(true)와 유사한 루프를 생성합니다. 루프 본문 프로세스가 실행될 때마다 Tick이 됩니다.
  • 각 Tick의 과정은 처리할 이벤트가 있는지 확인하는 것입니다. 이벤트가 있는 경우 이벤트 및 관련 콜백 함수를 제거합니다. 그런 다음 다음 루프에 들어가고 더 이상 처리할 이벤트가 없으면 프로세스를 종료합니다.

Nodejs 인터뷰 질문에 답할 수 있나요?

2.2 각 틱 동안 처리해야 할 이벤트가 있는지 어떻게 판단하나요?

    각 이벤트 루프에는 하나 이상의 관찰자가 있으며, 처리해야 할 이벤트가 있는지 확인하는 프로세스는 이러한 관찰자에게 처리해야 할 이벤트가 있는지 묻는 것입니다.
  • Node에서 이벤트는 주로 네트워크 요청, 파일 I/O 등에서 발생합니다. 이러한 이벤트에 해당하는 관찰자에는 파일 I/O 관찰자와 네트워크 I/O 관찰자가 포함됩니다.
  • 이벤트 루프는 일반적인 생산자/소비자 모델입니다. 비동기 I/O, 네트워크 요청 등은 이벤트의 생성자로서 다양한 유형의 이벤트를 Node에 지속적으로 제공합니다. 이러한 이벤트는 해당 관찰자에게 전달되고 이벤트 루프는 관찰자로부터 이벤트를 꺼내 처리합니다.
  • Windows에서는 IOCP 기반으로 이 루프가 생성되고, *nix에서는 멀티스레딩 기반으로 생성됩니다.
2.3 전체 비동기 I/O 프로세스에 대해 설명해주세요

Nodejs 인터뷰 질문에 답할 수 있나요?

3 . V8 가비지 수집 메커니즘

3.1 V8의 메모리 사용량을 확인하는 방법

다음을 반환하는 process.memoryUsage()를 사용하세요.

{
  rss: 4935680,
  heapTotal: 1826816,
  heapUsed: 650472,
  external: 49879
}

heapTotal 및 heapUsed는 V8의 메모리 사용량을 나타냅니다. external은 V8에서 관리하고 Javascript에 바인딩된 C++ 개체의 메모리 사용량을 나타냅니다. 상주 세트 크기인 RSS는 이 프로세스에 할당된 물리적 메모리의 양입니다(할당된 전체 메모리의 일부). 이 물리적 메모리에는 힙, 스택 및 코드 세그먼트가 포함됩니다.

3.2 V8의 메모리 제한은 무엇입니까? V8은 왜 이렇게 설계되었나요?

64비트 시스템에서는 1.4GB이고 32비트 시스템에서는 0.7GB입니다. 1.5GB 가비지 수집 힙 메모리로 인해 V8은 50밀리초 이상 걸리고 비증분 가비지 수집에도 1초 이상 걸립니다. 이는 가비지 수집 중에 Javascript 스레드의 실행을 일시 중지시키는 이벤트입니다. 이로 인해 애플리케이션의 성능과 영향이 급락하게 됩니다.

3.3 V8的内存分代和回收算法请简单讲一讲

在V8中,主要将内存分为新生代和老生代两代。新生代中的对象存活时间较短的对象,老生代中的对象存活时间较长,或常驻内存的对象。

Nodejs 인터뷰 질문에 답할 수 있나요?

3.3.1 新生代

新生代中的对象主要通过Scavenge算法进行垃圾回收。这是一种采用复制的方式实现的垃圾回收算法。它将堆内存一份为二,每一部分空间成为semispace。在这两个semispace空间中,只有一个处于使用中,另一个处于闲置状态。处于使用状态的semispace空间称为From空间,处于闲置状态的空间称为To空间。

Nodejs 인터뷰 질문에 답할 수 있나요?

  • 当开始垃圾回收的时候,会检查From空间中的存活对象,这些存活对象将被复制到To空间中,而非存活对象占用的空间将会被释放。完成复制后,From空间和To空间发生角色对换。

  • 应为新生代中对象的生命周期比较短,就比较适合这个算法。

  • 当一个对象经过多次复制依然存活,它将会被认为是生命周期较长的对象。这种新生代中生命周期较长的对象随后会被移到老生代中。

3.3.2 老生代

老生代主要采取的是标记清除的垃圾回收算法。与Scavenge复制活着的对象不同,标记清除算法在标记阶段遍历堆中的所有对象,并标记活着的对象,只清理死亡对象。活对象在新生代中只占叫小部分,死对象在老生代中只占较小部分,这是为什么采用标记清除算法的原因。

3.3.3 标记清楚算法的问题

主要问题是每一次进行标记清除回收后,内存空间会出现不连续的状态

Nodejs 인터뷰 질문에 답할 수 있나요?

  • 这种内存碎片会对后续内存分配造成问题,很可能出现需要分配一个大对象的情况,这时所有的碎片空间都无法完成此次分配,就会提前触发垃圾回收,而这次回收是不必要的。
  • 为了解决碎片问题,标记整理被提出来。就是在对象被标记死亡后,在整理的过程中,将活着的对象往一端移动,移动完成后,直接清理掉边界外的内存。

3.3.4 哪些情况会造成V8无法立即回收内存

闭包和全局变量

3.3.5 请谈一下内存泄漏是什么,以及常见内存泄漏的原因,和排查的方法

什么是内存泄漏

  • 内存泄漏(Memory Leak)指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。
  • 如果内存泄漏的位置比较关键,那么随着处理的进行可能持有越来越多的无用内存,这些无用的内存变多会引起服务器响应速度变慢。
  • 严重的情况下导致内存达到某个极限(可能是进程的上限,如 v8 的上限;也可能是系统可提供的内存上限)会使得应用程序崩溃。 常见内存泄漏的原因 内存泄漏的几种情况:

一、全局变量

a = 10;  
//未声明对象。  
global.b = 11;  
//全局变量引用 
这种比较简单的原因,全局变量直接挂在 root 对象上,不会被清除掉。

二、闭包

function out() {  
    const bigData = new Buffer(100);  
    inner = function () {  
        
    }  
}

闭包会引用到父级函数中的变量,如果闭包未释放,就会导致内存泄漏。上面例子是 inner 直接挂在了 root 上,那么每次执行 out 函数所产生的 bigData 都不会释放,从而导致内存泄漏。

需要注意的是,这里举得例子只是简单的将引用挂在全局对象上,实际的业务情况可能是挂在某个可以从 root 追溯到的对象上导致的。

三、事件监听

Node.js 的事件监听也可能出现的内存泄漏。例如对同一个事件重复监听,忘记移除(removeListener),将造成内存泄漏。这种情况很容易在复用对象上添加事件时出现,所以事件重复监听可能收到如下警告:

emitter.setMaxListeners() to increase limit

例如,Node.js 中 Agent 的 keepAlive 为 true 时,可能造成的内存泄漏。当 Agent keepAlive 为 true 的时候,将会复用之前使用过的 socket,如果在 socket 上添加事件监听,忘记清除的话,因为 socket 的复用,将导致事件重复监听从而产生内存泄漏。

原理上与前一个添加事件监听的时候忘了清除是一样的。在使用 Node.js 的 http 模块时,不通过 keepAlive 复用是没有问题的,复用了以后就会可能产生内存泄漏。所以,你需要了解添加事件监听的对象的生命周期,并注意自行移除。

排查方法

想要定位内存泄漏,通常会有两种情况:

  • 对于只要正常使用就可以重现的内存泄漏,这是很简单的情况只要在测试环境模拟就可以排查了。

  • 对于偶然的内存泄漏,一般会与特殊的输入有关系。想稳定重现这种输入是很耗时的过程。如果不能通过代码的日志定位到这个特殊的输入,那么推荐去生产环境打印内存快照了。

  • 需要注意的是,打印内存快照是很耗 CPU 的操作,可能会对线上业务造成影响。 快照工具推荐使用 heapdump 用来保存内存快照,使用 devtool 来查看内存快照。

  • 使用 heapdump 保存内存快照时,只会有 Node.js 环境中的对象,不会受到干扰(如果使用 node-inspector 的话,快照中会有前端的变量干扰)。

  • PS:安装 heapdump 在某些 Node.js 版本上可能出错,建议使用 npm install heapdump -target=Node.js 版本来安装。

4、Buffer模块

4.1 新建Buffer会占用V8分配的内存吗

不会,Buffer属于堆外内存,不是V8分配的。

4.2 Buffer.alloc和Buffer.allocUnsafe的区别

Buffer.allocUnsafe创建的 Buffer 实例的底层内存是未初始化的。 新创建的 Buffer 的内容是未知的,可能包含敏感数据。 使用 Buffer.alloc() 可以创建以零初始化的 Buffer 实例。

4.3 Buffer的内存分配机制

为了高效的使用申请来的内存,Node采用了slab分配机制。slab是一种动态的内存管理机制。 Node以8kb为界限来来区分Buffer为大对象还是小对象,如果是小于8kb就是小Buffer,大于8kb就是大Buffer。

例如第一次分配一个1024字节的Buffer,Buffer.alloc(1024),那么这次分配就会用到一个slab,接着如果继续Buffer.alloc(1024),那么上一次用的slab的空间还没有用完,因为总共是8kb,1024+1024 = 2048个字节,没有8kb,所以就继续用这个slab给Buffer分配空间。

如果超过8kb,那么直接用C++底层地宫的SlowBuffer来给Buffer对象提供空间。

4.4 Buffer乱码问题

例如一个份文件test.md里的内容如下:

床前明月光,疑是地上霜,举头望明月,低头思故乡

我们这样读取就会出现乱码:

var rs = require('fs').createReadStream('test.md', {highWaterMark: 11});
// 床前明???光,疑???地上霜,举头???明月,???头思故乡

一般情况下,只需要设置rs.setEncoding('utf8')即可解决乱码问题

5、webSocket

5.1 webSocket与传统的http有什么优势

  • 客户端与服务器只需要一个TCP连接,比http长轮询使用更少的连接
  • webSocket服务端可以推送数据到客户端
  • 更轻量的协议头,减少数据传输量

5.2 webSocket协议升级时什么,能简述一下吗?

首先,WebSocket连接必须由浏览器发起,因为请求协议是一个标准的HTTP请求,格式如下:

GET ws://localhost:3000/ws/chat HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Origin: http://localhost:3000
Sec-WebSocket-Key: client-random-string
Sec-WebSocket-Version: 13

该请求和普通的HTTP请求有几点不同:

  • GET请求的地址不是类似/path/,而是以ws://开头的地址;
  • 请求头Upgrade: websocket和Connection: Upgrade表示这个连接将要被转换为WebSocket连接;
  • Sec-WebSocket-Key是用于标识这个连接,并非用于加密数据;
  • Sec-WebSocket-Version指定了WebSocket的协议版本。

随后,服务器如果接受该请求,就会返回如下响应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: server-random-string

该响应代码101表示本次连接的HTTP协议即将被更改,更改后的协议就是Upgrade: websocket指定的WebSocket协议。

6、https

6.1 https用哪些端口进行通信,这些端口分别有什么用

  • 443端口用来验证服务器端和客户端的身份,比如验证证书的合法性
  • 80端口用来传输数据(在验证身份合法的情况下,用来数据传输)

6.2 身份验证过程中会涉及到密钥, 对称加密,非对称加密,摘要的概念,请解释一下

  • 密钥:密钥是一种参数,它是在明文转换为密文或将密文转换为明文的算法中输入的参数。密钥分为对称密钥与非对称密钥,分别应用在对称加密和非对称加密上。

  • 对称加密:对称加密又叫做私钥加密,即信息的发送方和接收方使用同一个密钥去加密和解密数据。对称加密的特点是算法公开、加密和解密速度快,适合于对大数据量进行加密,常见的对称加密算法有DES、3DES、TDEA、Blowfish、RC5和IDEA。

  • 非对称加密:非对称加密也叫做公钥加密。非对称加密与对称加密相比,其安全性更好。对称加密的通信双方使用相同的密钥,如果一方的密钥遭泄露,那么整个通信就会被破解。而非对称加密使用一对密钥,即公钥和私钥,且二者成对出现。私钥被自己保存,不能对外泄露。公钥指的是公共的密钥,任何人都可以获得该密钥。用公钥或私钥中的任何一个进行加密,用另一个进行解密。

  • 摘要: 摘要算法又称哈希/散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。算法不可逆。

6.3 为什么需要CA机构对证书签名

如果不签名会存在中间人攻击的风险,签名之后保证了证书里的信息,比如公钥、服务器信息、企业信息等不被篡改,能够验证客户端和服务器端的“合法性”。

6.4 https验证身份也就是TSL/SSL身份验证的过程

简要图解如下

Nodejs 인터뷰 질문에 답할 수 있나요?

7、进程通信

7.1 请简述一下node的多进程架构

面对node单线程对多核CPU使用不足的情况,Node提供了child_process模块,来实现进程的复制,node的多进程架构是主从模式,如下所示:

Nodejs 인터뷰 질문에 답할 수 있나요?

var fork = require('child_process').fork;
var cpus = require('os').cpus();
for(var i = 0; i < cpus.length; i++){
    fork(&#39;./worker.js&#39;);
}

在linux中,我们通过ps aux | grep worker.js查看进程

Nodejs 인터뷰 질문에 답할 수 있나요?

这就是著名的主从模式,Master-Worker

7.2 请问创建子进程的方法有哪些,简单说一下它们的区别

创建子进程的方法大致有:

  • spawn(): 启动一个子进程来执行命令
  • exec(): 启动一个子进程来执行命令,与spawn()不同的是其接口不同,它有一个回调函数获知子进程的状况
  • execFlie(): 启动一个子进程来执行可执行文件
  • fork(): 与spawn()类似,不同电在于它创建Node子进程需要执行js文件
  • spawn()与exec()、execFile()不同的是,后两者创建时可以指定timeout属性设置超时时间,一旦创建的进程超过设定的时间就会被杀死
  • exec()与execFile()不同的是,exec()适合执行已有命令,execFile()适合执行文件。

7.3 请问你知道spawn在创建子进程的时候,第三个参数有一个stdio选项吗,这个选项的作用是什么,默认的值是什么。

  • 选项用于配置在父进程和子进程之间建立的管道。
  • 默认情况下,子进程的 stdin、 stdout 和 stderr 会被重定向到 ChildProcess 对象上相应的 subprocess.stdin、subprocess.stdout 和 subprocess.stderr 流。
  • 这相当于将 options.stdio 设置为 ['pipe', 'pipe', 'pipe']。

7.4 请问实现一个node子进程被杀死,然后自动重启代码的思路

  • 在创建子进程的时候就让子进程监听exit事件,如果被杀死就重新fork一下
var createWorker = function(){
    var worker = fork(__dirname + &#39;worker.js&#39;)
    worker.on(&#39;exit&#39;, function(){
        console.log(&#39;Worker&#39; + worker.pid + &#39;exited&#39;);
        // 如果退出就创建新的worker
        createWorker()
    })
}

7.5 在7.4的基础上,实现限量重启,比如我最多让其在1分钟内重启5次,超过了就报警给运维

  • 思路大概是在创建worker的时候,就判断创建的这个worker是否在1分钟内重启次数超过5次
  • 所以每一次创建worker的时候都要记录这个worker 创建时间,放入一个数组队列里面,每次创建worker都去取队列里前5条记录
  • 如果这5条记录的时间间隔小于1分钟,就说明到了报警的时候了

7.6 如何实现进程间的状态共享,或者数据共享

我自己没用过Kafka这类消息队列工具,问了java,可以用类似工具来实现进程间通信,更好的方法欢迎留言

8、中间件

8.1 如果使用过koa、egg这两个Node框架,请简述其中的中间件原理,最好用代码表示一下

Nodejs 인터뷰 질문에 답할 수 있나요?

  • 上面是在网上找的一个示意图,就是说中间件执行就像洋葱一样,最早use的中间件,就放在最外层。处理顺序从左到右,左边接收一个request,右边输出返回response
  • 一般的中间件都会执行两次,调用next之前为第一次,调用next时把控制传递给下游的下一个中间件。当下游不再有中间件或者没有执行next函数时,就将依次恢复上游中间件的行为,让上游中间件执行next之后的代码
  • 例如下面这段代码
const Koa = require(&#39;koa&#39;)
const app = new Koa()
app.use((ctx, next) => {
    console.log(1)
    next()
    console.log(3)
})
app.use((ctx) => {
    console.log(2)
})
app.listen(3001)

执行结果是

1=>2=>3

koa中间件实现源码大致思路如下:

// 注意其中的compose函数,这个函数是实现中间件洋葱模型的关键
// 场景模拟
// 异步 promise 模拟
const delay = async () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
    }, 2000);
  });
}
// 中间间模拟
const fn1 = async (ctx, next) => {
  console.log(1);
  await next();
  console.log(2);
}
const fn2 = async (ctx, next) => {
  console.log(3);
  await delay();
  await next();
  console.log(4);
}
const fn3 = async (ctx, next) => {
  console.log(5);
}

const middlewares = [fn1, fn2, fn3];

// compose 实现洋葱模型
const compose = (middlewares, ctx) => {
  const dispatch = (i) => {
    let fn = middlewares[i];
    if(!fn){ return Promise.resolve() }
    return Promise.resolve(fn(ctx, () => {
      return dispatch(i+1);
    }));
  }
  return dispatch(0);
}

compose(middlewares, 1);

9、其它

现在在重新过一遍node 12版本的主要API,有很多新发现,比如说

  • fs.watch这个模块,事件的回调函数有一个参数是触发的事件名称,但是呢,无论我增删改,都是触发rename事件(如果更改是update事件,删除delete事件,重命名是rename事件,这样语义明晰该多好)。后来网上找到一个node-watch模块,此模块增删改都有对应的事件, 并且还高效的支持递归watch 文件。
  • util模块有个promisify方法,可以让一个遵循异常优先的回调风格的函数,即 (err, value) => ... 回调函数是最后一个参数,返回一个返回值是一个 promise 版本的函数。
const util = require(&#39;util&#39;);
const fs = require(&#39;fs&#39;);

const stat = util.promisify(fs.stat);
stat(&#39;.&#39;).then((stats) => {
  // 处理 `stats`。
}).catch((error) => {
  // 处理错误。
});

9.1 杂想

  • crypto模块,可以考察基础的加密学知识,比如摘要算法有哪些(md5, sha1, sha256,加盐的md5,sha256等等),接着可以问如何用md5自己模拟一个加盐的md5算法, 接着可以问加密算法(crypto.createCiphe)中的aes,eds算法的区别,分组加密模式有哪些(比如ECB,CBC,为什么ECB不推荐),node里的分组加密模式是哪种(CMM),这些加密算法里的填充和向量是什么意思,接着可以问数字签名和https的流程(为什么需要CA,为什么要对称加密来加密公钥等等)
  • tcp/ip,可以问很多基础问题,比如链路层通过什么协议根据IP地址获取物理地址(arp),网关是什么,ip里的ICMP协议有什么用,tcp的三次握手,四次分手的过程是什么,tcp如何控制重发,网络堵塞TCP会怎么办等等,udp和tcp的区别,udp里的广播和组播是什么,组播在node里通过什么模块实现。
  • os,操作系统相关基础,io的流程是什么(从硬盘里读取数据到内核的内存中,然后内核的内存将数据传入到调用io的应用程序的进程内存中),冯诺依曼体系是什么,进程和线程的区别等等(我最近在看马哥linux教程,因为自己不是科班出身,听了很多基础的计算机知识,受益匪浅,建议去bilibili看)
  • linux相关操作知识(node涉及到后台,虽然是做中台,不涉及数据库,但是基本的linux操作还是要会的)
  • node性能监控(自己也正在学习中)
  • 测试,因为用的egg框架,有很完善的学习单元测试的文档,省略这部分
  • 数据库可以问一些比如事务的等级有哪些,mysql默认的事务等级是什么,会产生什么问题,然后考一些mysql查询的笔试题。。。和常用优化技巧,node的mysql的orm工具使用过没有。。。(比如我自己是看的尚硅谷mysql初级+高级视频,书是看的mysql必知必会,我自己出于爱好学习一下。。。没有实战过)

更多编程相关知识,请访问:编程入门!!

위 내용은 Nodejs 인터뷰 질문에 답할 수 있나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제