抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

记录NodeJS八股文

说说对Node.js的理解?优缺点?应用场景?

理解

  • 开源与跨平台的JavaScript运行时环境
  • 在浏览器外运行V8 JavaScript引擎(Google Chrome的内核), 利用事件驱动、非阻塞和异步输入输出模型等技术提高性能;

优点

  • 处理高并发场景性能更佳;
  • 适合I/O密集型应用;

缺点

  • 单线程, 不适合CPU密集型应用;
  • 只支持单核CPU, 不能充分利用CPU;
  • 可靠性低, 一旦代码某个环节崩溃, 整个系统都崩溃;

说说Node中的EventEmitter?如何实现一个EventEmitter?

Node采用了事件驱动机制, 而EventEmitter就是实现事件驱动的基础;

EventEmitter的基础上, Node几乎所有的模块都继承了这个类, 这些模块拥有了自己的事件, 可以绑定/触发监听器, 实现了异步操作;

1
2
3
4
5
6
7
8
9
10
11
12
13
// 实现一个自定义event
const EventEmitter = require('events');

class MyEmitter extends EventEmitter{}
const myEmitter = new MyEmitter();

function callback() {
console.log('触发了event事件');
}

myEmitter.on('event', callback);
myEmitter.emit('event');
myEmitter.removeListener('event', callback);

EventLoop在浏览器与NodeJS的区别

结论

  • NodeJS版本 >= 11之后就和浏览器保持一致;
  • NodeJS版本 <= 10, 浏览器和NodeJS环境下, 在microtask(微任务)任务队列的执行时机不同, 有不同的效果;

看题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function test() {
setTimeout(() => {
console.log('children2');
Promise.resolve().then(() => {
console.log('children2-1');
})
}, 0)

setTimeout(() => {
console.log('children3');
Promise.resolve.then(() => {
console.log('children3-1');
})
})
}

test();

答案

1
2
3
4
5
// node >= 11 && 浏览器
children2 children2-1 children3 children3-1

// node <= 10
children2 children3 children2-1 children3-1

解析

我们就记住浏览器的EventLoop的机制就可以, 如果问到在NodeJS环境下的执行是什么, 我们可以来解析一下

  1. NodeJS 版本 >= 11 以及浏览器下
  • 执行test函数;
  • 执行第一个setTimeout, 放入到宏任务中;
  • 执行第二个setTimeout, 放入到宏任务中;
  • 此刻微任务里面没有任务, 那么执行宏任务, 从队列的头部取出一个宏任务(第一个setTimeout);
  • 打印children2, 此刻遇见Promise的then, 是微任务, 放入到微任务队列里面;
  • 接下来我们发现微任务里面有任务了, 取微任务里面的任务, 打印出children2-1;
  • 此刻微任务里面没有任务, 那么继续执行宏任务, 从任务队列的头部取出一个宏任务(第二个setTimeout);
  • 打印children3, 此刻遇见Promise的then, 是微任务, 放入微任务队列里面;
  • 接下来我们发现微任务里面有任务了, 取微任务里面的任务, 打印children3-1;
  1. NodeJS版本 <= 10
  • 执行test函数;
  • 执行第一个setTimeout, 放入到宏任务里面;
  • 执行第二个setTimeout, 放入到宏任务里面;
  • 此刻微任务里面没有任务, 那么执行宏任务, 从队列的头部取出一个宏任务(第一个setTimeout);
  • 打印children2, 此刻遇见Promise的then, 是微任务, 放入到微任务队列里面;
  • 注意注意!!!此刻将会继续从队列的头部取出一个宏任务(第二个setTimeout);
  • 打印children3, 此刻遇见Promise的then, 是微任务, 放入到微任务队列里面;
  • 此刻微任务队列里有2个任务, 依次取出执行children2-1 children3-1;

大白话

它们的主要区别是执行微任务的时机不同, 假如此刻宏任务有2个任务AB, 此刻微任务队列什么都没有。如果是NodeJS版本 <= 10下, 在执行A任务的时候产生了微任务W, A任务执行完毕, 那么会继续执行B, B执行完成之后再去执行微任务。而NodeJS版本 >= 11 && 浏览器执行A任务, 产生了微任务, A执行完之后要去执行微任务。

简述什么是EventEmitter?

EventEmitter是Node.js中的一个核心模块, 主要用于处理事件相关的操作。它是事件驱动编程模式的一个实现, 提供了一种机制, 允许对象(也被事件发射器)发布(emit)事件, 而其他对象(也被称为监听器)可以订阅这些事件并定义当这些事件发生时应该执行的回调函数;

EventEmitter的主要功能包括事件的注册和监听、事件触发与传递参数, 以及事件的移除。通过调用EventEmitter的on或addListener方法, 可以将事件监听器绑定到特定的事件上。当使用EventEmitter的emit方法触发特定的事件时, 所有注册到该事件的监听器将会按照注册的顺序被调用, 并且可以传递参数给事件监听器。此外, 通过调用removeListener方法, 可以在不需要时移除特定事件的监听器;

EventEmitter是一种发布-订阅模式的实现, 也是一种观察者模式的实现。它充当了事件调度中心的角色, 是Node.js中实现异步操作的关键组件之一。

如何实现一个EventEmitter?

什么是EventEmitter?

在Node.js中, EventEmitter是事件驱动的基础, 几乎所有模块都继承自它。它实现了观察者模式, 其中被观察者维护一组观察者, 并在更新时通知观察者;

EventEmitter允许对象绑定和触发事件监听器, 实现异步操作。在Node.js中, 许多对象都会分发事件, 例如fs.readStream对象会在文件被打开时触发一个事件;

使用EventEmitter

Node.js的events模块提供了一个EventEmitter类, 可以通过继承它创建自定义事件对象。

基本使用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
const EventEmitter = require('events');

class MyEmitter extends EventEmitter{};

const myEmitter = new MyEmitter();

function callback() {
console.log('触发了event事件');
}

myEmitter.on('event', callback);
myEmitter.emit('event');
myEmitter.removeListener('event', callback);

EventEmitter的常用方法有:

  • on(eventName, listener):添加类型为eventName的事件监听器;
  • once(eventName, listener):添加类型为eventName的事件监听器, 但只能执行一次, 执行后将被删除;
  • prependListener(eventName, listener):添加类型为eventName的事件监听器到事件数组头部;
  • emit(eventName, ...args):触发类型为eventName的事件监听器;
  • removeListener(eventName, listener):移除类型为eventName的事件监听器;

实现自定义EventEmitter

我们可以简单实现一个EventEmitter类, 了解其基本原理。在实现过程中, 需要维护一个包含所有事件的对象events

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class EventEmitter {
constructor() {
this.events = {};
}

on(type, handler) {
if (!this.events[type]) {
this.events = [];
}
this.events[type].push(handler);
}

emit(type, ...args) {
if (!this.events[type]) {
return;
}
this.events[type].forEach((handler) => {
handler.apply(this, args);
})
}

removeListener(type, handler) {
if (!this.events[type]) {
return;
}
this.events[type] = this.events[type].filter((item) => item != handler);
}
}

测试自定义EventEmitter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const bus = new EventEmitter();

bus.on('event', () => {
console.log('Event 1')
})

function event2() {
console.log('Event 2');
}

bus.on('event', event2);

bus.emit('event');
// Output:
// Event 1
// Event 2

bus.removeLister('event', event2);
bus.emit('event');
// Output:
// Event 1

总结

EventEmitter是Node.js中非常重要的一个模块, 它实现了事件驱动的基本模式, 让Node.js具备了处理异步操作的能力。

评论