当前位置: 首页 > news >正文

Node.js面试题及详细答案120题(01-15) -- 基础概念篇

前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。

前后端面试题-专栏总目录

在这里插入图片描述

文章目录

  • 一、本文面试题目录
      • 1. 什么是Node.js?它的特点是什么?
      • 2. Node.js和浏览器的JavaScript有什么区别?
      • 3. Node.js的运行环境由哪些部分组成?
      • 4. 为什么Node.js适合处理高并发场景?
      • 5. Node.js的事件循环(Event Loop)是什么?它的执行流程是怎样的?
      • 6. 事件循环的六个阶段分别是什么?每个阶段做什么?
      • 7. 什么是V8引擎?它在Node.js中的作用是什么?
      • 8. Node.js的单线程模型是指什么?它有什么优缺点?
      • 9. Node.js中的全局对象有哪些?和浏览器的全局对象有何不同?
      • 10. 什么是进程(Process)和线程(Thread)?Node.js如何处理多线程?
      • 11. Node.js的模块化系统遵循什么规范?和ES6模块有什么区别?
      • 12. `require`方法的加载机制是怎样的?
      • 13. `module.exports`和`exports`的区别是什么?
      • 14. 什么是CommonJS模块?它的优缺点是什么?
      • 15. Node.js中的`__dirname`和`__filename`有什么作用?
  • 二、120道Node.js面试题目录列表

一、本文面试题目录

1. 什么是Node.js?它的特点是什么?

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,允许开发者在服务器端使用 JavaScript 编写代码。它不是一门编程语言,也不是一个框架,而是一个让 JavaScript 能够脱离浏览器运行的平台。

主要特点:

  • 非阻塞 I/O 模型:在执行 I/O 操作(如文件读写、网络请求)时不会阻塞线程,提高了程序的执行效率。
  • 事件驱动:通过事件循环机制处理各种事件(如请求、数据到达等),实现高效的并发处理。
  • 单线程:主线程是单线程的,但通过事件循环和底层线程池处理并发任务。
  • 跨平台:可在 Windows、Linux、macOS 等多个操作系统上运行。
  • 丰富的生态系统:npm(Node Package Manager)提供了海量的第三方模块,方便开发者快速构建应用。

示例:使用 Node.js 创建一个简单的服务器

const http = require('http');const server = http.createServer((req, res) => {res.writeHead(200, { 'Content-Type': 'text/plain' });res.end('Hello, Node.js!\n');
});server.listen(3000, () => {console.log('Server running at http://localhost:3000/');
});

2. Node.js和浏览器的JavaScript有什么区别?

虽然 Node.js 和浏览器都使用 JavaScript 作为编程语言,但它们在运行环境、用途和特性上有显著区别:

区别Node.js浏览器JavaScript
运行环境服务器端(独立运行,无需浏览器)客户端(嵌入在浏览器中)
核心用途构建服务器、后端服务、工具脚本等实现网页交互、DOM操作、前端逻辑
API 差异提供文件系统(fs)、网络(http)等模块提供 DOM、BOM、Canvas 等浏览器相关API
全局对象全局对象为 global全局对象为 window
模块系统遵循 CommonJS 规范(使用 require主要使用 ES6 模块(import/export
事件处理基于 EventEmitter 实现事件驱动基于 DOM 事件模型(如 onclick
I/O 操作支持非阻塞 I/O 操作受浏览器安全限制,I/O 能力有限

示例:Node.js 中读取文件(浏览器无法直接实现)

const fs = require('fs');
fs.readFile('example.txt', 'utf8', (err, data) => {if (err) throw err;console.log(data);
});

3. Node.js的运行环境由哪些部分组成?

Node.js 的运行环境主要由以下核心组件构成:

  1. V8 引擎:Google 开发的 JavaScript 引擎,负责解析和执行 JavaScript 代码,提供高效的代码编译和运行能力。
  2. Libuv:一个跨平台的异步 I/O 库,提供事件循环、非阻塞 I/O 操作(如文件系统、网络)、线程池等核心功能。
  3. Node.js 核心模块:由 C++ 和 JavaScript 编写的内置模块,如 httpfspath 等,封装了底层功能。
  4. 第三方模块:通过 npm 安装的开源模块,扩展了 Node.js 的功能。
  5. 事件循环(Event Loop):Node.js 处理非阻塞 I/O 操作的核心机制,负责调度回调函数的执行。
  6. 线程池:由 Libuv 管理,用于处理耗时的 I/O 操作(如文件读写、DNS 解析),避免阻塞事件循环。

工作流程:当 Node.js 执行代码时,V8 引擎解析 JavaScript,遇到 I/O 操作时交给 Libuv 处理,Libuv 通过事件循环和线程池管理这些操作,完成后将结果通过回调函数返回给 V8 引擎执行。

4. 为什么Node.js适合处理高并发场景?

Node.js 适合高并发场景的核心原因在于其非阻塞 I/O 模型事件驱动机制,具体表现为:

  1. 非阻塞 I/O:当执行 I/O 操作(如数据库查询、网络请求)时,Node.js 不会阻塞主线程,而是将操作交给底层线程池处理,主线程可以继续处理其他任务。

  2. 事件驱动:通过事件循环机制,Node.js 能够高效地监听和处理各种事件(如请求到达、数据返回),回调函数在事件触发时才会被执行,避免了资源浪费。

  3. 单线程优势:主线程是单线程的,避免了多线程切换的开销(如上下文切换、锁竞争),适合处理大量轻量级的并发请求(如 API 接口调用)。

  4. 高吞吐量:对于 I/O 密集型任务(如 Web 服务器、实时通信),Node.js 能在有限的资源下处理更多请求,吞吐量远高于传统多线程模型。

示例:Node.js 处理高并发请求(无需等待前一个请求完成)

const http = require('http');// 模拟一个耗时的I/O操作(非阻塞)
const asyncOperation = (callback) => {setTimeout(() => {callback('Operation done');}, 100);
};const server = http.createServer((req, res) => {asyncOperation((result) => {res.end(result);});
});server.listen(3000, () => {console.log('Server running on port 3000');
});

注意:Node.js 不适合 CPU 密集型任务(如大规模计算),因为单线程会被长时间阻塞,导致并发能力下降。

5. Node.js的事件循环(Event Loop)是什么?它的执行流程是怎样的?

事件循环是 Node.js 处理非阻塞 I/O 操作的核心机制,允许单线程的 Node.js 执行非阻塞操作。它不断从事件队列中取出任务并执行,实现了异步代码的有序执行。

执行流程

  1. 执行同步代码,将异步任务(如 setTimeoutfs.readFile)放入对应的队列(宏任务队列或微任务队列)。
  2. 同步代码执行完毕后,进入事件循环。
  3. 依次执行当前微任务队列中的所有任务(直到清空)。
  4. 进入事件循环的某个阶段,执行该阶段的任务队列中的任务。
  5. 执行完该阶段任务后,检查是否有微任务,若有则重复步骤 3。
  6. 重复步骤 4-5,直到所有任务队列清空。

示例:事件循环执行顺序演示

console.log('同步代码开始');setTimeout(() => {console.log('setTimeout(宏任务)');
}, 0);Promise.resolve().then(() => {console.log('Promise.then(微任务)');
});console.log('同步代码结束');// 输出顺序:
// 同步代码开始
// 同步代码结束
// Promise.then(微任务)
// setTimeout(宏任务)

6. 事件循环的六个阶段分别是什么?每个阶段做什么?

Node.js 的事件循环分为六个阶段,按顺序依次执行,每个阶段都有一个任务队列:

  1. timers(定时器阶段)

    • 执行 setTimeout()setInterval() 调度的回调函数。
    • 检查是否有到期的定时器,若有则执行其回调。
  2. pending callbacks(待定回调阶段)

    • 执行延迟到下一个循环迭代的 I/O 回调(如某些系统操作的回调)。
  3. idle, prepare

    • 内部使用,开发者几乎不会涉及。
  4. poll(轮询阶段)

    • 检索新的 I/O 事件(如文件读写、网络请求)。
    • 执行 I/O 相关的回调(除了 timers、close callbacks 等)。
    • 若轮询队列不为空,依次执行回调;若为空,等待新的事件到达或进入下一个阶段。
  5. check(检查阶段)

    • 执行 setImmediate() 调度的回调函数(立即执行,优先级高于 setTimeout)。
  6. close callbacks(关闭回调阶段)

    • 执行关闭相关的回调(如 socket.on('close', ...))。

示例setImmediatesetTimeout 的执行顺序

// 在 I/O 回调中,setImmediate 先于 setTimeout 执行
const fs = require('fs');
fs.readFile(__filename, () => {setTimeout(() => {console.log('setTimeout');}, 0);setImmediate(() => {console.log('setImmediate');});
});
// 输出:setImmediate → setTimeout

7. 什么是V8引擎?它在Node.js中的作用是什么?

V8 是由 Google 开发的开源 JavaScript 引擎,采用 C++ 编写,最初用于 Chrome 浏览器,后被 Node.js 采用作为其 JavaScript 执行引擎。

V8 在 Node.js 中的作用

  1. 代码解析与编译:将 JavaScript 代码解析为抽象语法树(AST),再通过 JIT(即时编译)将 AST 转换为机器码,提高执行效率。
  2. 内存管理:负责 JavaScript 对象的内存分配、垃圾回收(采用分代回收机制)。
  3. 执行代码:运行 JavaScript 代码,包括同步代码、回调函数等。
  4. 提供基础 API:如 ObjectArray 等内置对象和方法。

特点

  • 高效的 JIT 编译:将热点代码(频繁执行的代码)编译为机器码,避免解释执行的性能损耗。
  • 快速的垃圾回收:采用分代回收策略,对年轻代和老年代对象分别进行回收,减少停顿时间。

Node.js 结合 V8 引擎和 Libuv 库,既实现了高效的 JavaScript 执行,又提供了非阻塞 I/O 能力。

8. Node.js的单线程模型是指什么?它有什么优缺点?

Node.js 的单线程模型是指其主线程(执行 JavaScript 代码的线程)是单线程的,即同一时间只能执行一个任务。但 Node.js 底层通过 Libuv 管理的线程池(默认4个线程)处理 I/O 操作,实现了并发。

优点

  • 减少线程开销:避免了多线程切换的上下文切换成本和锁竞争问题。
  • 简化编程模型:开发者无需处理多线程同步问题,降低了代码复杂度。
  • 高效处理 I/O 密集型任务:适合处理大量并发的 I/O 请求(如 API 服务、实时通信)。

缺点

  • 不适合 CPU 密集型任务:长时间的 CPU 计算会阻塞主线程,导致事件循环无法执行,影响整个应用的响应。
  • 单线程崩溃风险:主线程一旦抛出未捕获的异常,可能导致整个应用崩溃。
  • 无法充分利用多核 CPU:单线程只能使用一个 CPU 核心,需通过 Cluster 模块实现多核利用。

示例:CPU 密集型任务阻塞事件循环

// 耗时的计算函数(阻塞主线程)
function heavyCalculation() {let result = 0;for (let i = 0; i < 1e9; i++) {result += i;}return result;
}console.log('开始计算');
heavyCalculation();
console.log('计算结束'); // 需等待计算完成才会执行,期间无法处理其他请求
No.大剑师精品GIS教程推荐
0地图渲染基础- 【WebGL 教程】 - 【Canvas 教程】 - 【SVG 教程】
1Openlayers 【入门教程】 - 【源代码+示例 300+】
2Leaflet 【入门教程】 - 【源代码+图文示例 150+】
3MapboxGL【入门教程】 - 【源代码+图文示例150+】
4Cesium 【入门教程】 - 【源代码+综合教程 200+】
5threejs【中文API】 - 【源代码+图文示例200+】
6Shader 编程 【图文示例 100+】

9. Node.js中的全局对象有哪些?和浏览器的全局对象有何不同?

Node.js 中的全局对象是 global,类似于浏览器中的 window,但提供的属性和方法有所不同。

Node.js 主要全局对象/属性

  • global:全局对象本身,所有全局变量都是其属性。
  • console:用于输出日志(如 console.log)。
  • process:提供当前 Node.js 进程的信息和控制方法(如 process.envprocess.exit())。
  • Buffer:用于处理二进制数据。
  • setTimeout/setInterval/clearTimeout:定时器函数。
  • setImmediate/clearImmediate:立即执行的回调函数。
  • require:加载模块的函数。
  • module/exports:模块相关对象。
  • __dirname/__filename:当前模块的目录和文件路径。

与浏览器全局对象的区别

  • 浏览器的全局对象是 window,Node.js 是 global
  • 浏览器中全局变量自动成为 window 的属性,Node.js 中只有显式赋值给 global 的变量才是全局的。
  • 浏览器提供 documentlocation 等 DOM/BOM API,Node.js 没有。
  • Node.js 提供 processBuffer 等服务器端特有的全局对象,浏览器没有。

示例:Node.js 中使用全局对象

console.log(global === this); // true(在模块顶层,this 指向 module.exports,此处为 false;在函数中 this 指向 global)
console.log(__dirname); // 当前模块所在目录
console.log(process.pid); // 当前进程 ID

10. 什么是进程(Process)和线程(Thread)?Node.js如何处理多线程?

进程(Process):操作系统分配资源的基本单位,拥有独立的内存空间、文件描述符等,进程间相互隔离。

线程(Thread):进程内的执行单元,共享进程的资源(如内存),一个进程可以包含多个线程,线程间切换成本低于进程。

Node.js 的多线程处理

  • Node.js 主线程是单线程的(执行 JavaScript 代码),但底层通过 Libuv 管理一个线程池(默认4个线程),用于处理耗时的 I/O 操作(如文件读写、DNS 解析)。
  • 对于 CPU 密集型任务,可通过 child_process 模块创建子进程,或使用 worker_threads 模块创建工作线程,实现多线程并行处理。

child_process 示例:创建子进程

const { fork } = require('child_process');// 创建子进程
const child = fork('./child.js');// 主进程与子进程通信
child.on('message', (msg) => {console.log('主进程收到:', msg);
});
child.send('Hello from parent');

worker_threads 示例:创建工作线程

const { Worker } = require('worker_threads');const worker = new Worker(`const { parentPort } = require('worker_threads');parentPort.postMessage('Hello from worker');
`, { eval: true });worker.on('message', (msg) => {console.log('主线程收到:', msg);
});

11. Node.js的模块化系统遵循什么规范?和ES6模块有什么区别?

Node.js 的模块化系统默认遵循 CommonJS 规范,通过 require 加载模块,module.exports 导出模块。ES6 模块(ESM)是 JavaScript 官方的模块化标准,使用 importexport

CommonJS 与 ES6 模块的区别

区别CommonJSES6 模块
加载方式require('module')import module from 'module'
导出方式module.exportsexportsexportexport default
加载时机运行时动态加载(同步)编译时静态分析(可异步)
值的传递拷贝导出的值(原始值)或引用(对象)导出值的引用(动态绑定)
this 指向模块内 this 指向 module.exports模块内 this 指向 undefined
文件扩展名默认 .js.json.node通常使用 .mjs 或在 package.json 中指定 "type": "module"

CommonJS 示例

// 导出(module.js)
module.exports = {foo: 'bar',add: (a, b) => a + b
};// 导入(main.js)
const mod = require('./module');
console.log(mod.foo); // 'bar'

ES6 模块示例

// 导出(module.mjs)
export const foo = 'bar';
export default (a, b) => a + b;// 导入(main.mjs)
import add, { foo } from './module.mjs';
console.log(foo); // 'bar'
console.log(add(1, 2)); // 3

注意:Node.js 中使用 ES6 模块需将文件扩展名改为 .mjs,或在 package.json 中设置 "type": "module"

12. require方法的加载机制是怎样的?

require 是 Node.js 中用于加载模块的函数,其加载机制遵循以下流程:

  1. 缓存检查:优先从缓存中加载模块(require.cache),若已加载则直接返回缓存的模块,避免重复加载。

  2. 判断模块类型

    • 核心模块(如 fshttp):直接加载 Node.js 内置的核心模块,优先级最高。
    • 路径模块:以 ./..// 开头的路径,按路径查找文件。
    • 第三方模块:非路径模块,从当前目录的 node_modules 文件夹开始查找,若未找到则向上级目录的 node_modules 查找,直到根目录或找到模块。
  3. 文件查找规则

    • 若路径指向文件,直接加载(支持 .js.json.node 扩展名,可省略)。
    • 若路径指向目录,查找该目录下的 package.json 中的 main 字段指定的入口文件;若没有 package.jsonmain 字段,默认加载 index.js
  4. 编译执行:找到模块后,Node.js 会将其编译为函数并执行,最终返回 module.exports 的值。

示例require 加载不同类型的模块

// 加载核心模块
const fs = require('fs');// 加载路径模块(相对路径)
const myModule = require('./myModule');// 加载第三方模块(需先通过 npm 安装)
const lodash = require('lodash');

13. module.exportsexports的区别是什么?

module.exportsexports 都是 Node.js 中用于导出模块内容的对象,它们的关系和区别如下:

  • 关系exportsmodule.exports 的引用(即 exports = module.exports),初始时指向同一个空对象。
  • 区别
    • module.exports 是模块实际导出的对象,require 最终返回的是 module.exports 的值。
    • 若直接给 exports 赋值(如 exports = { foo: 'bar' }),会切断其与 module.exports 的关联,导致导出失效;而修改 module.exports 则会直接改变导出结果。

正确用法示例

// 方式1:修改 exports 的属性(推荐)
exports.foo = 'bar';
exports.add = (a, b) => a + b;// 方式2:修改 module.exports(适用于导出单个值)
module.exports = (a, b) => a + b;// 错误用法:直接赋值 exports 会导致导出失效
exports = { foo: 'bar' }; // 外部 require 无法获取该对象

注意:若同时使用 exportsmodule.exportsmodule.exports 的值会覆盖 exports 的属性(因为最终导出的是 module.exports)。

14. 什么是CommonJS模块?它的优缺点是什么?

CommonJS 是一套用于 JavaScript 模块化的规范,主要用于服务器端(如 Node.js),定义了模块的导入、导出和依赖管理方式。

核心规范

  • 每个文件是一个独立的模块,拥有独立的作用域。
  • 通过 module.exportsexports 导出模块内容。
  • 通过 require() 导入其他模块。
  • 模块加载是同步的,且加载后会被缓存。

优点

  • 简单易用:语法简洁,易于理解和使用。
  • 同步加载:适合服务器端环境,模块文件存储在本地,同步加载性能影响小。
  • 缓存机制:模块加载后会被缓存,提高重复加载的效率。
  • 广泛支持:Node.js 原生支持,是后端 JavaScript 模块化的事实标准。

缺点

  • 同步加载不适合浏览器:浏览器加载模块需从网络获取,同步加载会阻塞页面渲染,因此浏览器端更适合异步加载的 ES6 模块。
  • 动态加载限制require 可以在代码任意位置调用(动态加载),但不利于静态分析和 Tree-Shaking(消除未使用的代码)。
  • 循环依赖处理复杂:当模块间存在循环依赖时,可能导致部分导出内容未定义。

示例:CommonJS 模块的循环依赖

// a.js
const b = require('./b');
console.log('a.js 中 b.foo:', b.foo);
module.exports = { foo: 'a' };// b.js
const a = require('./a');
console.log('b.js 中 a.foo:', a.foo); // 输出 undefined(此时 a 模块尚未完成导出)
module.exports = { foo: 'b' };

15. Node.js中的__dirname__filename有什么作用?

__dirname__filename 是 Node.js 模块中的全局变量(实际是模块作用域的变量,而非 global 的属性),用于获取当前模块的路径信息。

  • __dirname:返回当前模块所在目录的绝对路径(字符串)。
  • __filename:返回当前模块文件的绝对路径(包括文件名)。

特点

  • 路径是绝对路径,不受执行 Node.js 命令的工作目录影响。
  • 仅在模块文件中有效,在 REPL 环境或非模块文件中未定义。
  • 可结合 path 模块处理路径(如拼接、解析)。

示例

const path = require('path');console.log('当前文件路径:', __filename);
// 输出:/home/user/project/main.jsconsole.log('当前目录路径:', __dirname);
// 输出:/home/user/project// 拼接路径
const configPath = path.join(__dirname, 'config', 'app.json');
console.log('配置文件路径:', configPath);
// 输出:/home/user/project/config/app.json

注意:在 ES6 模块(.mjs"type": "module")中,__dirname__filename 未定义,需通过 import.meta.url 手动计算:

import { fileURLToPath } from 'url';
import { dirname } from 'path';const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

二、120道Node.js面试题目录列表

文章序号Node.js面试题120道
1Node.js面试题及详细答案120道(01-15)
2Node.js面试题及详细答案120道(16-30)
3Node.js面试题及详细答案120道(31-42)
4Node.js面试题及详细答案120道(43-55)
5Node.js面试题及详细答案120道(56-68)
6Node.js面试题及详细答案120道(69-80)
7Node.js面试题及详细答案120道(81-92)
8Node.js面试题及详细答案120道(93-100)
9Node.js面试题及详细答案120道(101-110)
10Node.js面试题及详细答案120道(111-120)
http://www.dtcms.com/a/324749.html

相关文章:

  • Leaflet地图高亮与编辑功能实现
  • 【最后203篇系列】031 构建MCP尝试
  • signed 和 unsigned 类型说明符
  • 嵌套-列表存储字典,字典存储列表,字典存储字典
  • Linux 路由子系统深度分析:框架、实现与代码路径
  • web刷题3
  • 商业解决方案技术栈总结
  • Python 获取对象信息的所有方法
  • 基于Spring Boot和SSE的实时消息推送系统
  • 三数之和 Java
  • 人工智能系列(7)人工神经网络中的无监督学习
  • C语言-数组和指针练习题合集(一)
  • C语言深度剖析
  • 网页五子棋测试
  • VUE+SPRINGBOOT从0-1打造前后端-前后台系统-关于我们
  • 2025最新免费的大模型和免费的大模型API有哪些?(202508更新)
  • 秋招春招实习百度笔试百度管培生笔试题库百度非技术岗笔试|笔试解析和攻略|题库分享
  • 冒泡排序实现以及优化
  • WebSocket集群方案解析与实现
  • My APK 安卓版:高效管理手机应用的工具软件
  • windows的cmd命令【持续更新】
  • Linux应用软件编程---文件操作1(fopen、fclose、fgetc/fputc、fgets/fputs)
  • 什么是浏览器标识?
  • 【Docker进阶实战】从多容器编排到集群部署
  • TSF应用开发与运维部署
  • 个人笔记Mybatis2
  • 医学统计(现况调查的统计分析策略1)
  • 电脑使用“碎片整理”程序的作用
  • 基于ECharts的智慧社区数据可视化
  • 【npm、yarn、pnpm】特点对比,按需选择