Node.js面试题及详细答案120题(93-100) -- 错误处理与调试篇
《前后端面试题
》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。
文章目录
- 一、本文面试题目录
- 93. Node.js中的错误类型有哪些?如何捕获全局错误?
- 常见错误类型
- 捕获全局错误
- 94. 如何使用`try/catch`处理异步错误?它能捕获哪些错误?
- 处理异步错误的方式
- `try/catch` 能捕获的错误
- 95. 什么是未捕获异常(Uncaught Exception)?如何处理?
- 示例:未捕获异常
- 处理方式
- 96. 什么是未处理的Promise拒绝(Unhandled Rejection)?如何处理?
- 示例:未处理的Promise拒绝
- 处理方式
- 97. Node.js的调试工具有哪些?如何使用`--inspect`参数调试?
- 常用调试工具
- 使用`--inspect`参数调试
- 98. 如何使用VS Code调试Node.js程序?
- 步骤1:创建调试配置
- 步骤2:配置`launch.json`
- 步骤3:开始调试
- 99. 什么是日志?在Node.js中如何记录和管理日志?
- 日志的作用
- 记录和管理日志的方式
- 100. 常用的Node.js日志库有哪些?请举例说明。
- 1. `winston`
- 2. `pino`
- 3. `morgan`
- 二、120道Node.js面试题目录列表
一、本文面试题目录
93. Node.js中的错误类型有哪些?如何捕获全局错误?
Node.js 中的错误可分为多种类型,不同类型对应不同的场景,捕获全局错误能避免程序意外崩溃。
常见错误类型
Error
:所有错误的基类,包含错误消息(message
)和堆栈跟踪(stack
)。SyntaxError
:语法错误(如代码拼写错误),通常在程序启动时抛出。// 示例:缺少括号导致语法错误 function test() { console.log('test') // 缺少 }
TypeError
:类型错误(如调用非函数对象)。const num = 123; num(); // TypeError: num is not a function
ReferenceError
:引用错误(如使用未定义的变量)。console.log(undefinedVar); // ReferenceError: undefinedVar is not defined
RangeError
:范围错误(如数组长度为负数)。const arr = new Array(-1); // RangeError: Invalid array length
- 自定义错误:通过继承
Error
实现特定业务错误。class ValidationError extends Error {constructor(message) {super(message);this.name = 'ValidationError';} }
捕获全局错误
全局错误指未被局部 try/catch
捕获的错误,需通过事件监听处理:
// 1. 捕获未捕获的同步异常
process.on('uncaughtException', (err) => {console.error('未捕获的同步异常:', err);// 必要时进行资源清理后退出进程process.exit(1);
});// 2. 捕获未处理的Promise拒绝
process.on('unhandledRejection', (reason, promise) => {console.error('未处理的Promise拒绝:', reason);// 可在此处记录日志或进行补救
});// 3. 捕获警告(非致命错误)
process.on('warning', (warning) => {console.warn('警告:', warning.message);
});
注意:uncaughtException
发生后,进程可能处于不稳定状态,建议记录日志后重启进程。
94. 如何使用try/catch
处理异步错误?它能捕获哪些错误?
try/catch
是 JavaScript 处理异常的标准方式,在 Node.js 中可结合 async/await
处理异步错误,但需注意其适用范围。
处理异步错误的方式
-
async/await
+try/catch
(推荐):const fs = require('fs/promises');async function readFile() {try {// 异步操作:读取文件const data = await fs.readFile('nonexistent.txt', 'utf8');console.log(data);} catch (err) {// 捕获异步操作中的错误(如文件不存在)console.error('读取失败:', err.message);} }readFile();
-
Promise链式调用 +
catch()
:fs.readFile('nonexistent.txt', 'utf8').then(data => console.log(data)).catch(err => console.error('读取失败:', err.message));
try/catch
能捕获的错误
-
同步错误:如
TypeError
、ReferenceError
等。 -
await
后的异步错误:通过async/await
包装的 Promise 拒绝。 -
异步回调中的错误(不能直接捕获):需在回调内部处理。
// 错误示例:try/catch 无法捕获回调中的异步错误 try {fs.readFile('file.txt', (err, data) => {if (err) throw err; // 此处错误不会被外部 try/catch 捕获}); } catch (err) {console.error('无法捕获此处错误'); }// 正确处理:在回调内部捕获 fs.readFile('file.txt', (err, data) => {try {if (err) throw err;console.log(data);} catch (err) {console.error('处理错误:', err);} });
95. 什么是未捕获异常(Uncaught Exception)?如何处理?
未捕获异常(Uncaught Exception) 指同步代码中抛出的错误未被 try/catch
捕获,导致错误冒泡到事件循环顶层,可能导致程序崩溃。
示例:未捕获异常
// 同步代码抛出错误但未捕获
function throwError() {throw new Error('致命错误');
}throwError(); // 未被捕获,触发 uncaughtException 事件
console.log('此处不会执行');
处理方式
通过监听 process
对象的 uncaughtException
事件捕获:
// 注册全局未捕获异常处理器
process.on('uncaughtException', (err) => {// 1. 记录详细错误日志(包含堆栈)console.error('未捕获异常:', err.stack);// 2. 清理资源(如关闭数据库连接、释放文件句柄)const cleanup = async () => {try {await db.disconnect(); // 假设 db 是数据库连接对象console.log('资源清理完成');} catch (cleanupErr) {console.error('清理失败:', cleanupErr);}};// 3. 清理后退出进程(避免不稳定状态)cleanup().then(() => {process.exit(1); // 非0退出码表示异常终止});
});
最佳实践:
- 仅用
uncaughtException
作为最后的安全网,优先通过try/catch
局部处理。 - 发生未捕获异常后,进程状态可能不可靠,应重启进程(可配合进程管理工具如 PM2 自动重启)。
96. 什么是未处理的Promise拒绝(Unhandled Rejection)?如何处理?
未处理的Promise拒绝(Unhandled Rejection) 指Promise被拒绝(reject
)后,未通过 catch()
或 try/catch
处理,导致错误未被捕获。
示例:未处理的Promise拒绝
// 情况1:Promise链式调用未加 catch()
Promise.reject(new Error('操作失败')).then(() => console.log('成功')); // 未处理拒绝// 情况2:async/await 未用 try/catch
async function test() {await Promise.reject(new Error('异步失败')); // 未捕获
}
test();
处理方式
-
局部处理:为每个Promise添加
catch()
或用try/catch
。// 方式1:使用 catch() Promise.reject(new Error('失败')).catch(err => console.error('处理拒绝:', err));// 方式2:使用 try/catch async function safeTest() {try {await Promise.reject(new Error('失败'));} catch (err) {console.error('处理拒绝:', err);} } safeTest();
-
全局处理:监听
unhandledRejection
事件(作为兜底)。process.on('unhandledRejection', (reason, promise) => {console.error('未处理的Promise拒绝:', reason);// 可选:给未处理的Promise添加catch(避免多次触发)promise.catch(err => console.error('补捕获:', err)); });
注意:Node.js 15+ 中,未处理的Promise拒绝会导致进程退出(类似未捕获异常),因此必须处理。
97. Node.js的调试工具有哪些?如何使用--inspect
参数调试?
Node.js 提供多种调试工具,用于定位代码错误和性能问题,--inspect
参数是官方推荐的调试方式。
常用调试工具
- Chrome DevTools:通过
--inspect
连接,支持断点、变量监视等。 - VS Code 内置调试器:集成IDE,操作便捷。
- WebStorm:专业JavaScript IDE,调试功能强大。
node-inspect
:命令行调试工具(需单独安装)。ndb
:改进的命令行调试工具(来自Google)。
使用--inspect
参数调试
-
启动调试模式:
# 基础用法:启动程序并开启调试 node --inspect app.js# 可选:指定端口(默认9229) node --inspect=3000 app.js# 可选:程序启动时暂停(等待调试器连接) node --inspect-brk app.js
-
连接调试器:
- 打开 Chrome 浏览器,访问
chrome://inspect
。 - 在“Remote Target”中找到目标程序,点击“inspect”打开调试面板。
- 打开 Chrome 浏览器,访问
-
调试操作:
- 设置断点:在代码行号处点击添加断点。
- 监视变量:在“Watch”面板输入变量名。
- 控制流程:使用“继续”“单步执行”“步入”“步出”按钮控制代码执行。
示例:调试 app.js
// app.js
function add(a, b) {return a + b; // 在该行设置断点
}const result = add(2, 3);
console.log(result);
启动命令:node --inspect app.js
,在 Chrome DevTools 中观察变量 a
、b
的值。
98. 如何使用VS Code调试Node.js程序?
VS Code 内置Node.js调试器,支持断点调试、变量监视等功能,操作步骤如下:
步骤1:创建调试配置
- 打开项目文件夹,点击左侧“运行和调试”图标(或按
Ctrl+Shift+D
)。 - 点击“创建 launch.json 文件”,选择“Node.js”环境,自动生成配置文件。
步骤2:配置launch.json
基础配置示例:
{"version": "0.2.0","configurations": [{"type": "node","request": "launch","name": "启动程序","program": "${workspaceFolder}/app.js" // 入口文件路径},{"type": "node","request": "attach","name": "附加到进程","port": 9229 // 对应 --inspect 端口}]
}
launch
:直接启动程序并调试。attach
:附加到已运行的--inspect
进程。
步骤3:开始调试
- 在代码行号左侧点击设置断点(出现红色圆点)。
- 点击调试面板的“启动调试”按钮(或按
F5
)。 - 使用调试控制栏操作:
- 继续(
F5
):执行到下一个断点。 - 单步跳过(
F10
):执行当前行,不进入函数。 - 单步调试(
F11
):进入当前函数内部。 - 步出(
Shift+F11
):从当前函数退出。
- 继续(
- 在“变量”面板查看当前作用域的变量,在“监视”面板添加自定义监视表达式。
示例:调试HTTP服务器
// app.js
const http = require('http');
const server = http.createServer((req, res) => {const url = req.url; // 设置断点res.end(`Hello ${url}`);
});
server.listen(3000);
启动调试后,访问 http://localhost:3000/test
,程序会在断点处暂停,可查看 req.url
的值。
99. 什么是日志?在Node.js中如何记录和管理日志?
日志是程序运行过程中产生的记录,用于跟踪流程、排查错误、分析性能。在Node.js中,日志管理需考虑分级、格式、存储和轮转。
日志的作用
- 错误排查:记录异常堆栈和上下文。
- 行为跟踪:记录用户操作、系统事件。
- 性能分析:记录耗时操作的执行时间。
- 安全审计:记录敏感操作(如登录、权限变更)。
记录和管理日志的方式
-
基础日志记录:使用
console
方法(简单但功能有限)。console.log('信息日志:用户登录'); // 普通信息 console.warn('警告日志:磁盘空间不足'); // 警告 console.error('错误日志:数据库连接失败'); // 错误
-
使用日志库:如
winston
或pino
,支持分级、格式化和输出到文件。const winston = require('winston'); const logger = winston.createLogger({level: 'info', // 日志级别(info及以上会被记录)format: winston.format.combine(winston.format.timestamp(), // 添加时间戳winston.format.json() // JSON格式),transports: [new winston.transports.Console(), // 输出到控制台new winston.transports.File({ filename: 'error.log', level: 'error' }), // 错误日志到文件new winston.transports.File({ filename: 'combined.log' }) // 所有日志到文件] });// 使用日志 logger.info('用户登录', { userId: 1, ip: '192.168.1.1' }); logger.error('支付失败', { orderId: 100, error: '余额不足' });
-
日志轮转:防止日志文件过大,使用
winston-daily-rotate-file
按时间分割日志。npm install winston-daily-rotate-file
const DailyRotateFile = require('winston-daily-rotate-file'); logger.add(new DailyRotateFile({filename: 'app-%DATE%.log',datePattern: 'YYYY-MM-DD',maxSize: '20m', // 单个文件最大20MBmaxFiles: '14d' // 保留14天日志 }));
-
日志级别:按重要性从低到高为
silly
、debug
、verbose
、info
、warn
、error
,可通过配置控制输出粒度。
100. 常用的Node.js日志库有哪些?请举例说明。
Node.js 生态中有多个成熟的日志库,各有特点,适用于不同场景:
1. winston
- 特点:功能全面、可扩展、支持多传输目标(控制台、文件、数据库等)。
- 示例:
const winston = require('winston');const logger = winston.createLogger({level: 'debug', // 记录debug及以上级别format: winston.format.combine(winston.format.colorize(), // 控制台彩色输出winston.format.simple() // 简化格式),transports: [new winston.transports.Console(),new winston.transports.File({ filename: 'app.log' })] });logger.debug('调试信息:变量x的值为10'); logger.info('用户注册成功'); logger.warn('API响应缓慢'); logger.error('数据库连接失败', { error: '连接超时' });
2. pino
- 特点:高性能(速度比
winston
快5-10倍)、默认JSON格式、适合生产环境。 - 示例:
const pino = require('pino'); const logger = pino({level: 'info',timestamp: pino.stdTimeFunctions.isoTime // ISO格式时间戳 });// 输出到文件(需配合重定向) // const logger = pino(pino.destination('app.log'));logger.info({ userId: 1 }, '用户登录'); logger.error({ err: new Error('失败') }, '操作出错');
3. morgan
- 特点:专为HTTP请求日志设计,常与Express/Koa配合使用。
- 示例:
const express = require('express'); const morgan = require('morgan'); const app = express();// 输出到控制台(combined格式包含详细信息) app.use(morgan('combined'));// 输出到文件 const fs = require('fs'); const accessLogStream = fs.createWriteStream('access.log', { flags: 'a' }); app.use(morgan('combined', { stream: accessLogStream }));app.get('/',
二、120道Node.js面试题目录列表
文章序号 | Node.js面试题120道 |
---|---|
1 | Node.js面试题及详细答案120道(01-15) |
2 | Node.js面试题及详细答案120道(16-30) |
3 | Node.js面试题及详细答案120道(31-42) |
4 | Node.js面试题及详细答案120道(43-55) |
5 | Node.js面试题及详细答案120道(56-68) |
6 | Node.js面试题及详细答案120道(69-80) |
7 | Node.js面试题及详细答案120道(81-92) |
8 | Node.js面试题及详细答案120道(93-100) |
9 | Node.js面试题及详细答案120道(101-110) |
10 | Node.js面试题及详细答案120道(111-120) |