Node.js 核心特性面试题
Node.js 核心特性涵盖事件循环、模块系统、异步编程等后端开发的关键概念。
🔥 Node.js 核心面试题
1. 事件循环和异步编程模型
问题:详细解释Node.js的事件循环机制,以及与浏览器事件循环的区别。
参考答案:
javascript
// Node.js 事件循环阶段
console.log('=== Node.js 事件循环示例 ===');
// 1. 同步代码
console.log('1. 同步代码开始');
// 2. timer阶段 - setTimeout
setTimeout(() => {
console.log('4. setTimeout - timer阶段');
}, 0);
// 3. I/O阶段 - setImmediate
setImmediate(() => {
console.log('5. setImmediate - check阶段');
});
// 4. process.nextTick - 微任务队列优先级最高
process.nextTick(() => {
console.log('2. process.nextTick - 微任务');
});
// 5. Promise - 微任务队列
Promise.resolve().then(() => {
console.log('3. Promise.then - 微任务');
});
console.log('1. 同步代码结束');
// 事件循环详细实现
class SimpleEventLoop {
constructor() {
this.timerQueue = [];
this.immediateQueue = [];
this.nextTickQueue = [];
this.promiseQueue = [];
this.running = false;
}
// 模拟setTimeout
setTimeout(callback, delay) {
const timer = {
callback,
executeTime: Date.now() + delay
};
this.timerQueue.push(timer);
this.timerQueue.sort((a, b) => a.executeTime - b.executeTime);
}
// 模拟setImmediate
setImmediate(callback) {
this.immediateQueue.push(callback);
}
// 模拟process.nextTick
nextTick(callback) {
this.nextTickQueue.push(callback);
}
// 模拟Promise
resolvePromise(callback) {
this.promiseQueue.push(callback);
}
// 执行微任务
flushMicrotasks() {
// process.nextTick 优先级最高
while (this.nextTickQueue.length > 0) {
const callback = this.nextTickQueue.shift();
callback();
}
// Promise 微任务
while (this.promiseQueue.length > 0) {
const callback = this.promiseQueue.shift();
callback();
}
}
// 事件循环主体
run() {
this.running = true;
while (this.running) {
// 1. Timer 阶段
const now = Date.now();
while (this.timerQueue.length > 0 && this.timerQueue[0].executeTime <= now) {
const timer = this.timerQueue.shift();
timer.callback();
this.flushMicrotasks(); // 每个宏任务后执行微任务
}
// 2. I/O 回调阶段(简化)
// 这里会处理网络、文件系统等I/O回调
// 3. Check 阶段 - setImmediate
while (this.immediateQueue.length > 0) {
const callback = this.immediateQueue.shift();
callback();
this.flushMicrotasks(); // 每个宏任务后执行微任务
}
// 如果没有待执行的任务,退出循环
if (this.timerQueue.length === 0 &&
this.immediateQueue.length === 0 &&
this.nextTickQueue.length === 0 &&
this.promiseQueue.length === 0) {
this.running = false;
}
}
}
}
// 实际应用:异步流控制
class AsyncFlowControl {
// 串行执行异步任务
static async series(tasks) {
const results = [];
for (const task of tasks) {
const result = await task();
results.push(result);
}
return results;
}
// 并行执行异步任务
static async parallel(tasks) {
return Promise.all(tasks.map(task => task()));
}
// 限制并发数的并行执行
static async parallelLimit(tasks, limit) {
const results = [];
const executing = [];
for (const task of tasks) {
const promise = task().then(result => {
results.push(result);
executing.splice(executing.indexOf(promise), 1);
return result;
});
results.push(promise);
executing.push(promise);
if (executing.length >= limit) {
await Promise.race(executing);
}
}
await Promise.all(executing);
return Promise.all(results);
}
// 瀑布流执行
static async waterfall(tasks, initialValue) {
let result = initialValue;
for (const task of tasks) {
result = await task(result);
}
return result;
}
}
// 使用示例
async function demonstrateAsyncFlow() {
const createAsyncTask = (name, delay) => {
return () => new Promise(resolve => {
setTimeout(() => {
console.log(`任务 ${name} 完成`);
resolve(`结果${name}`);
}, delay);
});
};
const tasks = [
createAsyncTask('A', 1000),
createAsyncTask('B', 500),
createAsyncTask('C', 800)
];
console.log('串行执行:');
const seriesResults = await AsyncFlowControl.series(tasks);
console.log('串行结果:', seriesResults);
console.log('并行执行:');
const parallelResults = await AsyncFlowControl.parallel([
createAsyncTask('D', 1000),
createAsyncTask('E', 500),
createAsyncTask('F', 800)
]);
console.log('并行结果:', parallelResults);
console.log('限制并发(2):');
const limitResults = await AsyncFlowControl.parallelLimit([
createAsyncTask('G', 1000),
createAsyncTask('H', 500),
createAsyncTask('I', 800),
createAsyncTask('J', 300)
], 2);
console.log('限制并发结果:', limitResults);
}2. 模块系统和包管理
问题:Node.js的模块系统工作原理,CommonJS与ES Modules的区别?
参考答案:
javascript
// 1. CommonJS 模块系统
// math.js
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
class Calculator {
constructor() {
this.history = [];
}
calculate(operation, a, b) {
let result;
switch (operation) {
case 'add':
result = add(a, b);
break;
case 'multiply':
result = multiply(a, b);
break;
default:
throw new Error('不支持的操作');
}
this.history.push({ operation, a, b, result });
return result;
}
}
// 导出方式1:module.exports
module.exports = {
add,
multiply,
Calculator
};
// 导出方式2:exports(注意限制)
// exports.add = add;
// exports.multiply = multiply;
// 导出方式3:混合导出
module.exports = Calculator;
module.exports.add = add;
module.exports.multiply = multiply;
// 2. 模块加载和缓存机制
// main.js
const math = require('./math');
const math2 = require('./math'); // 从缓存中加载
console.log(math === math2); // true - 同一个引用
// 模块加载路径解析
// require('./math') -> 相对路径
// require('/abs/math') -> 绝对路径
// require('lodash') -> node_modules
// require('math/calc') -> node_modules/math/calc
// 3. ES Modules (Node.js 14+)
// math.mjs 或 package.json中type: "module"
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
export default class Calculator {
constructor() {
this.history = [];
}
}
// 命名导出和重新导出
export { add as sum, multiply };
export * from './utilities.mjs';
// main.mjs
import Calculator, { add, multiply as mult } from './math.mjs';
import * as mathUtils from './math.mjs';
// 动态导入
const math = await import('./math.mjs');
// 4. 模块系统实现原理
class ModuleSystem {
constructor() {
this.cache = new Map();
}
require(id) {
// 1. 检查缓存
if (this.cache.has(id)) {
return this.cache.get(id).exports;
}
// 2. 创建模块对象
const module = {
id,
exports: {},
loaded: false,
children: [],
parent: null
};
// 3. 加入缓存
this.cache.set(id, module);
// 4. 加载模块内容
const source = this.loadSource(id);
// 5. 包装代码
const wrapper = `
(function(exports, require, module, __filename, __dirname) {
${source}
});
`;
// 6. 编译执行
const compiledWrapper = eval(wrapper);
const require = (childId) => this.require(childId);
compiledWrapper.call(
module.exports,
module.exports,
require,
module,
id,
path.dirname(id)
);
// 7. 标记已加载
module.loaded = true;
return module.exports;
}
loadSource(id) {
// 模拟读取文件内容
const fs = require('fs');
return fs.readFileSync(id, 'utf8');
}
}
// 5. 包管理实践
// package.json配置
const packageConfig = {
"name": "my-node-app",
"version": "1.0.0",
"type": "module", // 启用ES Modules
"main": "index.js",
"exports": {
".": {
"import": "./index.mjs",
"require": "./index.cjs"
},
"./utils": {
"import": "./utils/index.mjs",
"require": "./utils/index.cjs"
}
},
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "jest",
"build": "tsc"
},
"dependencies": {
"express": "^4.18.0",
"lodash": "^4.17.21"
},
"devDependencies": {
"nodemon": "^2.0.15",
"jest": "^27.5.1"
},
"engines": {
"node": ">=14.0.0"
}
};
// 6. 高级模块模式
// 单例模式
class DatabaseConnection {
constructor() {
if (DatabaseConnection.instance) {
return DatabaseConnection.instance;
}
this.connected = false;
DatabaseConnection.instance = this;
}
connect() {
if (!this.connected) {
console.log('建立数据库连接');
this.connected = true;
}
return this;
}
}
module.exports = new DatabaseConnection();
// 工厂模式
class LoggerFactory {
static createLogger(type) {
switch (type) {
case 'console':
return new ConsoleLogger();
case 'file':
return new FileLogger();
case 'database':
return new DatabaseLogger();
default:
throw new Error(`不支持的日志类型: ${type}`);
}
}
}
// 依赖注入容器
class DIContainer {
constructor() {
this.services = new Map();
this.singletons = new Map();
}
register(name, factory, options = {}) {
this.services.set(name, { factory, options });
}
resolve(name) {
const service = this.services.get(name);
if (!service) {
throw new Error(`服务未注册: ${name}`);
}
if (service.options.singleton) {
if (!this.singletons.has(name)) {
this.singletons.set(name, service.factory(this));
}
return this.singletons.get(name);
}
return service.factory(this);
}
}
// 使用依赖注入
const container = new DIContainer();
container.register('database', () => new DatabaseConnection(), { singleton: true });
container.register('userService', (container) => {
return new UserService(container.resolve('database'));
}, { singleton: true });
const userService = container.resolve('userService');3. 流(Streams)和文件系统
问题:Node.js中流的概念和应用场景,以及文件系统操作的最佳实践?
参考答案:
javascript
const fs = require('fs');
const { Readable, Writable, Transform, pipeline } = require('stream');
const { promisify } = require('util');
const path = require('path');
// 1. 自定义可读流
class NumberStream extends Readable {
constructor(options, max = 10) {
super(options);
this.current = 1;
this.max = max;
}
_read() {
if (this.current <= this.max) {
this.push(`数字: ${this.current}\n`);
this.current++;
} else {
this.push(null); // 结束流
}
}
}
// 2. 自定义可写流
class UpperCaseWriter extends Writable {
constructor(options) {
super(options);
}
_write(chunk, encoding, callback) {
const upperCased = chunk.toString().toUpperCase();
process.stdout.write(upperCased);
callback();
}
}
// 3. 自定义转换流
class JSONTransform extends Transform {
constructor(options) {
super(options);
this.objectMode = true;
}
_transform(chunk, encoding, callback) {
try {
const obj = JSON.parse(chunk.toString());
obj.timestamp = new Date().toISOString();
obj.processed = true;
this.push(JSON.stringify(obj) + '\n');
callback();
} catch (error) {
callback(error);
}
}
}
// 4. 流的实际应用
class FileProcessor {
// 大文件读取处理
static async processLargeFile(inputPath, outputPath) {
return new Promise((resolve, reject) => {
const readStream = fs.createReadStream(inputPath, {
encoding: 'utf8',
highWaterMark: 16 * 1024 // 16KB chunks
});
const writeStream = fs.createWriteStream(outputPath);
const transformer = new Transform({
transform(chunk, encoding, callback) {
// 处理每个数据块
const processed = chunk.toString()
.replace(/old/g, 'new')
.toUpperCase();
callback(null, processed);
}
});
// 使用pipeline确保错误处理
pipeline(readStream, transformer, writeStream, (error) => {
if (error) {
console.error('Pipeline失败:', error);
reject(error);
} else {
console.log('Pipeline成功完成');
resolve();
}
});
});
}
// CSV数据处理
static processCSV(inputPath) {
const readStream = fs.createReadStream(inputPath, { encoding: 'utf8' });
let buffer = '';
return new Promise((resolve, reject) => {
const results = [];
readStream.on('data', (chunk) => {
buffer += chunk;
const lines = buffer.split('\n');
// 保留最后一行(可能不完整)
buffer = lines.pop();
lines.forEach(line => {
if (line.trim()) {
const data = line.split(',');
results.push({
id: data[0],
name: data[1],
email: data[2]
});
}
});
});
readStream.on('end', () => {
// 处理最后的buffer
if (buffer.trim()) {
const data = buffer.split(',');
results.push({
id: data[0],
name: data[1],
email: data[2]
});
}
resolve(results);
});
readStream.on('error', reject);
});
}
// 文件上传处理
static handleFileUpload(req, uploadDir) {
return new Promise((resolve, reject) => {
const filename = `upload_${Date.now()}.tmp`;
const filepath = path.join(uploadDir, filename);
const writeStream = fs.createWriteStream(filepath);
let totalSize = 0;
const maxSize = 10 * 1024 * 1024; // 10MB限制
req.on('data', (chunk) => {
totalSize += chunk.length;
if (totalSize > maxSize) {
writeStream.destroy();
fs.unlink(filepath, () => {});
reject(new Error('文件太大'));
return;
}
writeStream.write(chunk);
});
req.on('end', () => {
writeStream.end();
resolve({ filepath, filename, size: totalSize });
});
req.on('error', (error) => {
writeStream.destroy();
fs.unlink(filepath, () => {});
reject(error);
});
});
}
}
// 5. 文件系统操作封装
class FileSystemUtils {
// 递归创建目录
static async ensureDir(dirPath) {
try {
await fs.promises.access(dirPath);
} catch (error) {
await fs.promises.mkdir(dirPath, { recursive: true });
}
}
// 递归删除目录
static async removeDir(dirPath) {
try {
const files = await fs.promises.readdir(dirPath);
for (const file of files) {
const filePath = path.join(dirPath, file);
const stat = await fs.promises.stat(filePath);
if (stat.isDirectory()) {
await this.removeDir(filePath);
} else {
await fs.promises.unlink(filePath);
}
}
await fs.promises.rmdir(dirPath);
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
}
// 复制文件
static async copyFile(source, destination) {
await this.ensureDir(path.dirname(destination));
return new Promise((resolve, reject) => {
const readStream = fs.createReadStream(source);
const writeStream = fs.createWriteStream(destination);
pipeline(readStream, writeStream, (error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}
// 获取目录大小
static async getDirSize(dirPath) {
let totalSize = 0;
const files = await fs.promises.readdir(dirPath);
for (const file of files) {
const filePath = path.join(dirPath, file);
const stat = await fs.promises.stat(filePath);
if (stat.isDirectory()) {
totalSize += await this.getDirSize(filePath);
} else {
totalSize += stat.size;
}
}
return totalSize;
}
// 文件监控
static watchDirectory(dirPath, callback) {
const watcher = fs.watch(dirPath, { recursive: true }, (eventType, filename) => {
if (filename) {
const filePath = path.join(dirPath, filename);
callback(eventType, filePath);
}
});
return watcher;
}
// 安全的JSON文件读写
static async readJSONFile(filePath) {
try {
const data = await fs.promises.readFile(filePath, 'utf8');
return JSON.parse(data);
} catch (error) {
if (error.code === 'ENOENT') {
return null;
}
throw error;
}
}
static async writeJSONFile(filePath, data) {
await this.ensureDir(path.dirname(filePath));
const jsonData = JSON.stringify(data, null, 2);
await fs.promises.writeFile(filePath, jsonData, 'utf8');
}
}
// 6. 使用示例
async function demonstrateStreams() {
// 创建流
const numberStream = new NumberStream({}, 5);
const upperWriter = new UpperCaseWriter();
// 管道操作
numberStream.pipe(upperWriter);
// 处理文件
try {
await FileProcessor.processLargeFile('input.txt', 'output.txt');
const csvData = await FileProcessor.processCSV('data.csv');
console.log('CSV数据:', csvData);
const dirSize = await FileSystemUtils.getDirSize('./uploads');
console.log(`上传目录大小: ${dirSize} 字节`);
// 监控文件变化
const watcher = FileSystemUtils.watchDirectory('./watched', (eventType, filePath) => {
console.log(`文件 ${eventType}: ${filePath}`);
});
// 5秒后停止监控
setTimeout(() => {
watcher.close();
}, 5000);
} catch (error) {
console.error('操作失败:', error);
}
}
// demonstrateStreams();这些Node.js核心特性面试题涵盖了服务端JavaScript开发的关键概念,展示了对Node.js生态系统的深入理解和实际应用能力。
