以下为JavaScript高级篇面试考察点总结,具体知识点不会太详细,主要梳理面试核心考察点,为面试做准备。高级JavaScript工程师的面试不再局限于API的使用或孤立的知识点,而是聚焦于对语言、引擎、生态以及软件工程思想的综合理解与掌控能力。
- 2025前端面试题-JS基础篇
- 2025前端面试题-JS中级篇
- 2025前端面试题-TS理论篇
- 2025前端面试题-TS实战篇
- 2025前端面试题-Vue3基础篇
- 2025前端面试题-Vue3进阶篇
- 2025前端面试题-React基础篇
- 2025前端面试题-React进阶篇
- 2025前端面试题-React高阶篇
一、 V8引擎工作原理与垃圾回收 (GC)
理解JavaScript的执行环境是高级优化的前提。
V8引擎核心流程
-
解析 (Parsing) : V8将JavaScript源代码解析成抽象语法树 (AST) 。
-
解释 (Interpretation) : Ignition (V8的解释器) 将AST转换成字节码并执行。同时,Ignition会收集分析信息,用于后续的优化。
-
编译 (Compilation) : 对于被频繁执行的代码(热点代码),TurboFan (V8的优化编译器) 会介入,利用分析信息将字节码编译成高度优化的机器码,以提升执行效率。这个过程被称为JIT (Just-In-Time) 编译 。如果优化的假设失败(如函数参数类型改变),会进行去优化 (Deoptimization) ,回退到字节码执行。
垃圾回收 (Garbage Collection)
V8采用分代回收 (Generational Collection)的策略,将堆内存分为 新生代 (New Generation)和老生代 (Old Generation) 。
新生代 (Scavenger算法)
- 空间小,存活对象少。采用Scavenger算法,将空间一分为二(From-Space 和 To-Space)。
- 回收时,将From-Space中的存活对象复制到To-Space,然后清空From-Space。最后,From-Space和To-Space角色互换。
- 对象若经历多轮回收仍存活,则被**晋升 (Promotion)**到老生代。
老生代
-
空间大,存活对象多。采用标记-清除算法。
-
标记阶段: 从根对象(如全局对象)开始,遍历所有可达对象并打上标记。
-
清除阶段: 清除非标记对象所占用的内存。
-
整理阶段 : 为解决内存碎片化问题,在清除后,会将所有存活对象向一端移动,形成连续的内存空间。
代码示例 (导致内存泄漏的场景):
高级开发者需要能够识别并解释内存泄漏。闭包引用了已分离的DOM节点是典型案例。
js
function createLeakingElement() {
const container = document.getElementById('container');
const detachedElement = document.createElement('div');
detachedElement.textContent = 'This is a potentially leaking element.';
container.appendChild(detachedElement);
// 关键:一个外部可访问的函数,通过闭包持有了对 detachedElement 的引用
const leakingClosure = function() {
// 即使 detachedElement 从DOM树中移除,只要 leakingClosure 存在,
// detachedElement 就不会被GC回收。
console.log(detachedElement.textContent);
};
// 从DOM中移除元素
container.removeChild(detachedElement);
// 返回这个闭包
return leakingClosure;
}
// globalLeaker 现在持有了对 detachedElement 的间接引用
// 即使它在DOM中已不可见,它依然存在于内存中
window.globalLeaker = createLeakingElement();
// 只要 window.globalLeaker 不被设为 null,这块内存就永远无法被回收
二、 事件循环 (Event Loop)
高级面试会深入到Node.js环境,考察对Event Loop各阶段的理解。
-
浏览器 vs. Node.js: 两者模型相似,但Node.js的事件循环有更明确的阶段划分。
-
Node.js 事件循环的六个阶段:
- timers : 执行
setTimeout()和setInterval()的回调。 - pending callbacks: 执行上一轮循环中延迟到本轮执行的I/O回调。
- idle, prepare: 仅内部使用。
- poll : 核心阶段。检索新的I/O事件;执行与I/O相关的回调。如果队列不为空,会遍历执行;如果为空,会在此阻塞等待,直到有新的I/O事件或到达
timers设定的阈值。 - check : 执行
setImmediate()的回调。 - close callbacks : 执行如
socket.on('close', ...)的回调。
- timers : 执行
-
process.nextTick()与微任务 (Micro-task) :process.nextTick()有自己独立的队列,其优先级高于所有微任务。- 在一个阶段执行完毕后,事件循环会立即 清空
nextTick队列,然后才清空微任务队列,之后才进入下一个阶段。
代码示例 (Node.js环境下):
js
const fs = require('fs');
console.log('1. Script Start');
// Timers 阶段
setTimeout(() => {
console.log('7. setTimeout');
}, 0);
// Check 阶段
setImmediate(() => {
console.log('8. setImmediate');
});
// Micro-task
Promise.resolve().then(() => {
console.log('5. Promise.then');
});
// process.nextTick 队列 (最高优先级)
process.nextTick(() => {
console.log('4. process.nextTick');
});
// I/O 操作,其回调将在 Poll 阶段执行
fs.readFile(__filename, () => {
console.log('6. I/O (readFile) callback');
// I/O回调内部的调度
setTimeout(() => console.log('11. I/O -> setTimeout'), 0);
setImmediate(() => console.log('9. I/O -> setImmediate'));
process.nextTick(() => console.log('10. I/O -> nextTick'));
});
console.log('2. Script End');
console.log('3. Poll phase may start here...');
// 理论输出顺序:
// 1. Script Start
// 2. Script End
// 3. Poll phase may start here...
// 4. process.nextTick
// 5. Promise.then
// 6. I/O (readFile) callback
// 10. I/O -> nextTick
// 9. I/O -> setImmediate
// 7. setTimeout
// 8. setImmediate
// 11. I/O -> setTimeout
// (注意:9, 7, 8, 11 的确切顺序可能因I/O耗时和系统调度而有细微变化,但基本规律如此)
三、 高级性能优化
Tree Shaking (摇树优化)
原理
- 依赖ES Modules (
import/export) 的静态结构,在编译时分析代码,移除未被实际引用的"死代码"(dead-code)。
实践
- Webpack, Rollup等现代打包工具在生产模式下默认开启。开发者需保证代码遵循ESM规范,并避免有副作用的模块导入。
Code Splitting (代码分割)
目的
- 将巨大的单体bundle分割成多个小块(chunks),按需加载,以减小首屏加载体积,提升用户体验。
策略
-
按路由分割: 每个页面或路由对应一个chunk。
-
按组件分割: 对于非首屏、或需要交互才出现的大型组件(如弹窗、图表)进行懒加载。
-
公共库分离 (Vendor Splitting) : 将不常变动的第三方库(如React, Lodash)打包成独立的vendor chunk,利用浏览器缓存。
利用浏览器渲染路径
-
关键渲染路径 : 优化CSS加载(内联关键CSS)、减少阻塞渲染的脚本、使用
async/defer。 -
硬件加速 : 尽量使用
transform和opacity属性进行动画,它们能被提升到单独的合成层(Compositor Layer),由GPU处理,避免触发重排(Reflow)和重绘(Repaint)。
代码示例 (React中的代码分割)
jsx
import React, { Suspense, lazy } from 'react';
// 使用 React.lazy 和动态 import() 来实现组件的懒加载
const HeavyComponent = lazy(() => import('./components/HeavyComponent'));
const AnotherLazyComponent = lazy(() => import('./components/AnotherLazyComponent'));
function App() {
const [showHeavy, setShowHeavy] = React.useState(false);
return (
<div>
<h1>My App</h1>
<button onClick={() => setShowHeavy(true)}>Load Heavy Component</button>
{/*
Suspense 组件用于在懒加载组件下载和解析期间,显示一个fallback UI。
只有当 showHeavy 为 true 时,浏览器才会去请求 HeavyComponent.js。
*/}
<Suspense fallback={<div>Loading...</div>}>
{showHeavy && <HeavyComponent />}
{/* 假设这是另一个需要懒加载的组件 */}
{/* <AnotherLazyComponent /> */}
</Suspense>
</div>
);
}
四、 内存管理与诊断
内存泄漏的常见原因
-
意外的全局变量: 未经声明的变量被赋值,成为全局对象的属性。
-
遗忘的定时器或回调 :
setInterval未被清除,其回调函数及其闭包环境无法被回收。 -
分离的DOM节点引用: 如第一节的代码示例。
-
闭包的滥用: 闭包会使其外部函数的作用域持续存在,如果作用域中包含大量数据,则可能造成内存占用过高。
诊断工具 (Chrome DevTools)
-
Performance Monitor: 实时监控CPU使用率、JS堆大小、DOM节点数等。
-
Memory Tab:
- Heap Snapshot (堆快照) : 拍摄堆内存的快照,用于分析对象分布、查找分离的DOM树、定位内存泄漏。
- Allocation Instrumentation on Timeline: 记录内存分配的时间线,用于定位是哪个函数或操作导致了频繁的内存分配或内存激增。
代码示例 (遗忘的定时器):
js
class PulsingDot {
constructor() {
this.size = 0;
this.isGrowing = true;
// 定时器通过闭包持有了对 this (PulsingDot实例) 的引用
this.intervalId = setInterval(() => {
if (this.isGrowing) {
this.size += 1;
if (this.size >= 10) this.isGrowing = false;
} else {
this.size -= 1;
if (this.size <= 0) this.isGrowing = true;
}
}, 100);
}
// 必须提供一个销毁方法来清除定时器
destroy() {
clearInterval(this.intervalId);
console.log('PulsingDot destroyed and interval cleared.');
}
}
let dot = new PulsingDot();
// 假设在某个时间点,我们不再需要这个 dot 实例
dot = null;
// 问题:虽然 dot 变量被设为 null,但 PulsingDot 实例无法被回收,
// 因为 setInterval 的回调函数仍然持有对它的引用,定时器还在不停地运行。
// 正确做法:在销毁对象前,调用 dot.destroy()。
五、 软件设计模式
高级开发者应能将设计模式思想融入日常编码,以构建可维护、可扩展的系统。
-
单例模式 (Singleton) : 确保一个类只有一个实例,并提供一个全局访问点。
-
观察者模式 (Observer / Pub/Sub) : 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知并自动更新。
-
工厂模式 (Factory) : 定义一个用于创建对象的接口,让子类决定实例化哪一个类。
-
装饰器模式 (Decorator) : 动态地给一个对象添加一些额外的职责。
-
代理模式 (Proxy) : 为其他对象提供一种代理以控制对这个对象的访问。
代码示例 (观察者模式/发布-订阅):
js
class EventBus {
constructor() {
this.listeners = {};
}
// 订阅
on(eventName, callback) {
if (!this.listeners[eventName]) {
this.listeners[eventName] = [];
}
this.listeners[eventName].push(callback);
}
// 取消订阅
off(eventName, callback) {
if (!this.listeners[eventName]) return;
this.listeners[eventName] = this.listeners[eventName].filter(
listener => listener !== callback
);
}
// 发布
emit(eventName, ...args) {
if (!this.listeners[eventName]) return;
this.listeners[eventName].forEach(listener => {
try {
listener(...args);
} catch (e) {
console.error(`Error in listener for event "${eventName}":`, e);
}
});
}
}
// --- 使用场景 ---
const bus = new EventBus();
function onUserLogin(userData) {
console.log('Analytics Service: User logged in', userData.name);
}
function updateNavbar(userData) {
console.log('UI Service: Updating navbar for', userData.name);
}
bus.on('user:login', onUserLogin);
bus.on('user:login', updateNavbar);
// 某处登录成功后...
bus.emit('user:login', { id: 1, name: 'Mickey' });
// 用户退出时,可以取消订阅
// bus.off('user:login', onUserLogin);
六、 模块化与工程化
模块化方案演进
从IIFE、CommonJS (require/module.exports)、AMD (define/require) 到 ES Modules (import/export) 。高级开发者需理解它们的差异及适用场景。
构建工具
Webpack
一个强大的、高度可配置的模块打包器。核心概念:Entry , Output , Loaders (转换非JS模块), Plugins (执行更广泛的任务,如打包优化、资源管理), Mode。
Vite
新一代前端构建工具。利用浏览器原生ESM支持,在开发环境下实现极速的冷启动和热更新 (HMR)。生产环境则使用Rollup进行打包。
Monorepo
在单一代码仓库中管理多个项目/包的策略。工具:Lerna, Nx, Turborepo。
代码示例 (一个基础的 webpack.config.js):
js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// 模式:'development' 或 'production'
mode: 'development',
// 入口文件
entry: './src/index.js',
// 输出配置
output: {
filename: 'bundle.[contenthash].js', // contenthash 用于缓存优化
path: path.resolve(__dirname, 'dist'),
clean: true, // 在生成文件之前清空 output 目录
},
// 模块处理规则
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: {
// 使用 babel-loader 来转换 ES6+ 语法
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react']
}
}
},
{
test: /.css$/,
// loader 的执行顺序是从右到左
use: ['style-loader', 'css-loader']
}
]
},
// 插件配置
plugins: [
// 自动生成一个 HTML 文件,并注入打包后的 JS
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
// 开发服务器配置
devServer: {
static: './dist',
hot: true,
},
// Source Map 配置,用于调试
devtool: 'eval-source-map',
};
七、 Web安全
XSS (Cross-Site Scripting)
攻击者将恶意脚本注入到网页中,其他用户浏览时执行。
类型
- 储存型(
Stored XSS) - 反射型(
Reflected XSS) - DOM型(
DOM-based XSS)。
防御
-
绝不信任任何用户输入。
-
输出编码/转义: 对用户输入的数据在渲染到页面前进行HTML实体转义。现代框架(如React, Vue)默认进行此操作。
-
使用
textContent代替innerHTML。 -
Content Security Policy (CSP) : 通过HTTP头,严格限制页面可以加载的资源来源。
CSRF (Cross-Site Request Forgery)
攻击者诱导已登录用户在不知情的情况下,向其已认证的Web应用发送一个伪造的请求(如转账、修改密码)。
防御
-
Anti-CSRF Token: 服务器为每个用户会话生成一个随机Token,要求所有状态变更的请求(POST, PUT, DELETE)都必须携带此Token。
-
SameSite Cookie 属性 : 将Cookie设置为
Strict或Lax,可以阻止浏览器在跨站请求中发送Cookie。SameSite=Strict是最强的防御。 -
检查 Referer 头: 验证请求的来源,但此方法可被伪造。
代码示例 (XSS防御):
js
const userInput = '<img src="invalid" onerror="alert('XSS Attack!')">';
// 错误的方式:直接使用 innerHTML
const vulnerableDiv = document.getElementById('vulnerable');
// vulnerableDiv.innerHTML = userInput; // 这将执行 onerror 中的恶意脚本
// 正确的方式:使用 textContent,浏览器会将其作为纯文本处理
const secureDiv = document.getElementById('secure');
secureDiv.textContent = userInput; // 页面将显示字符串 "<img..." 而非图片
八、 框架原理 (以React为例)
Virtual DOM (VDOM)
-
一个以JavaScript对象形式存在的、对真实DOM的抽象表示。
-
工作流:
Diffing差异比较) C --> D(计算出最小化的变更集); D --> E(将变更批量更新
到真实DOM);
Reconciliation (协调) 与 Diffing 算法
Diffing策略
-
Tree Diff: 只对同层级的节点进行比较,跨层级的移动会视为节点的销毁和重建。
-
Component Diff: 如果组件类型不同,直接销毁旧组件,创建新组件;如果类型相同,则更新其属性。
-
Element Diff : 对于同层级的一组子节点,通过
key属性进行优化。key帮助React识别哪些元素是稳定的、哪些是新增或删除的,从而实现高效的移动和复用,而不是原地销毁重建。
代码示例 (key的重要性)
js
// 场景:在一个列表的开头插入一个新元素
// --- 不推荐:使用 index 作为 key ---
// 当在开头插入 'grape' 时,列表变为 ['grape', 'apple', 'banana']
// React 看到:
// - key=0 的元素从 'apple' 变为 'grape' (更新)
// - key=1 的元素从 'banana' 变为 'apple' (更新)
// - 新增一个 key=2 的元素 'banana' (新增)
// 这导致了大量不必要的DOM更新。
const BadList = ({ items }) => (
<ul>{items.map((item, index) => <li key={index}>{item}</li>)}</ul>
);
// --- 推荐:使用稳定且唯一的ID作为 key ---
// 当在开头插入 {id: 3, text: 'grape'} 时
// React 看到:
// - key=1 ('apple') 和 key=2 ('banana') 的元素仍然存在,只需移动位置
// - 新增一个 key=3 的元素 'grape'
// 这只会导致一次新增操作和两次移动操作,效率极高。
const GoodList = ({ items }) => (
<ul>{items.map(item => <li key={item.id}>{item.text}</li>)}</ul>
);
九、 TypeScript
核心价值
- 为JavaScript带来静态类型系统
- 在编译阶段发现潜在错误
- 提升代码的可维护性、可读性和大型项目的健壮性。
高级类型
-
泛型 (Generics) : 创建可重用的、类型安全的组件或函数。
-
条件类型 (Conditional Types) :
T extends U ? X : Y,使类型可以根据条件变化。 -
映射类型 (Mapped Types) :
[K in keyof T]: ...,基于一个现有类型创建新类型。 -
工具类型 (Utility Types) :
Partial<T>,Required<T>,Readonly<T>,Pick<T, K>,Omit<T, K>等,对类型进行转换和操作。
代码示例 (泛型与条件类型)
ts
// 泛型函数:确保输入和输出类型一致
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("myString"); // output 类型为 string
// 泛型接口
interface GenericRepository<T> {
findById(id: number): Promise<T | null>;
findAll(): Promise<T[]>;
save(entity: T): Promise<T>;
}
// class UserRepository implements GenericRepository<User> { ... }
// 条件类型:从一个类型中提取特定类型的属性名
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
interface Part {
id: number;
name: string;
updatePart(newName: string): void;
getPartName(): string;
}
// Type 'FunctionNames' will be "updatePart" | "getPartName"
type FunctionNames = FunctionPropertyNames<Part>;
十、 Node.js与服务器端
Node.js不仅是"能在服务器上运行的JavaScript",更是一个基于事件驱动、非阻塞I/O模型的强大运行时环境。对其核心原理、生态系统及最佳实践的掌握,是构建高性能、高并发网络应用的基础。
1. Node.js 核心模型
事件驱动 & 非阻塞I/O
-
这是Node.js性能的基石。与传统的每个请求独占一个线程的模型不同,Node.js在单个主线程上运行事件循环。
-
当遇到I/O操作(如数据库查询、文件读写、网络请求)时,Node.js不会等待其完成,而是将操作和回调函数交给底层系统(如libuv库),然后继续处理事件队列中的其他事件。
-
当I/O操作完成后,其回调函数会被放回事件队列,等待事件循环的下一次轮询来执行。
适用场景
- 这种模型使得Node.js极其适合处理大量并发连接的I/O密集型应用,如实时聊天服务、API网关、微服务等。
但它天然不适合CPU密集型任务,因为长时间的计算会阻塞主线程,导致整个应用无响应。
2. Web框架:Express & Koa
虽然Node.js内置了http模块,但直接使用它来构建复杂的Web应用是繁琐且低效的。Web框架提供了路由、中间件、模板引擎集成等高级抽象。
Express
事实上的行业标准,以其稳定、灵活和庞大的社区生态而著称。其核心是中间件(Middleware) 概念------一系列按顺序处理请求的函数。
Koa
由Express原班人马打造,被视为下一代Node.js Web框架。Koa的核心是利用async/await语法,通过洋葱模型 (Onion Model) 来组织中间件,使得异步流程控制更为优雅和直观。
代码示例 (Express中间件与路由)
js
const express = require('express');
const app = express();
// 1. 应用级中间件:日志记录器
// 这是一个简单的中间件,会记录每个请求的方法、URL和时间戳。
app.use((req, res, next) => {
const now = new Date().toISOString();
console.log(`[${now}] ${req.method} ${req.originalUrl}`);
next(); // 关键:调用 next() 将控制权传递给下一个中间件或路由处理器
});
// 2. 内置中间件:用于解析JSON格式的请求体
app.use(express.json());
// 3. 路由处理器
app.get('/', (req, res) => {
res.send('Welcome to the homepage!');
});
app.post('/api/users', (req, res) => {
// req.body 是由 express.json() 中间件处理后得到的
const newUser = req.body;
console.log('Creating new user:', newUser);
// ... 在这里执行数据库插入等操作 ...
res.status(201).json({ id: Date.now(), ...newUser });
});
// 4. 错误处理中间件 (特殊的4个参数)
// 应该放在所有 app.use() 和路由调用的最后
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
3. Streams (流)
Stream是Node.js中处理流式数据的抽象接口。在处理大文件或大量网络数据时,一次性将所有数据读入内存是低效且危险的(可能导致内存溢出)。Stream允许我们以小块(chunks)的方式、边读取边处理数据。
四种基本类型
-
Readable : 可供读取数据的流 (如
fs.createReadStream)。 -
Writable : 可供写入数据的流 (如
fs.createWriteStream,http.ServerResponse)。 -
Duplex : 既可读又可写的流 (如
net.Socket)。 -
Transform : 在读写过程中可以修改或转换数据的Duplex流 (如
zlib.createGzip)。
pipe()方法
- 是连接流的最简单方式,它会自动处理数据从Readable流到Writable流的传输,并能妥善处理背压 (Back-pressure) 问题(即写入速度跟不上读取速度时,自动暂停读取)。
代码示例 (高效的文件服务器):
以下代码使用流高效地提供一个大文件的下载,而无需将整个文件加载到服务器内存中。
js
const http = require('http');
const fs = require('fs');
const path = require('path');
const server = http.createServer((req, res) => {
// 假设我们要提供一个名为 'large-video.mp4' 的文件下载
const filePath = path.join(__dirname, 'large-video.mp4');
// 检查文件是否存在
fs.stat(filePath, (err, stats) => {
if (err) {
res.writeHead(404, { 'Content-Type': 'text/plain' });
return res.end('File not found.');
}
// 设置响应头
res.writeHead(200, {
'Content-Type': 'video/mp4',
'Content-Length': stats.size
});
// 创建一个可读流
const readStream = fs.createReadStream(filePath);
// 关键:使用 pipe() 将文件流直接导入到HTTP响应流(res)
// Node.js 会自动处理数据分块、发送以及背压控制。
// 这是一种极其高效且内存友好的方式。
readStream.pipe(res);
// 监听错误事件
readStream.on('error', (streamErr) => {
console.error('Stream Error:', streamErr);
res.end(); // 发生错误时关闭连接
});
});
});
server.listen(3000, () => {
console.log('File server listening on port 3000');
});
4. Child Processes (子进程)
为了解决CPU密集型任务阻塞主线程的问题,Node.js提供了child_process模块,允许创建子进程来执行这些任务。
-
spawn(): 启动一个新进程,以流的方式进行I/O,适合处理大量数据。 -
exec(): 启动一个shell来执行命令,将stdout/stderr缓存起来,在进程结束时通过回调一次性返回。有大小限制,适合执行简单的shell命令。 -
fork():spawn()的一个特殊变体,专门用于创建新的Node.js进程。父子进程之间会建立一个IPC (Inter-Process Communication) 通道,允许通过.send()和.on('message', ...)来收发消息。
代码示例 (使用fork处理CPU密集型计算):
假设我们需要进行一个耗时的斐波那契数列计算。
parent.js (主进程)
js
const { fork } = require('child_process');
console.log('Main process started.');
const child = fork('./child.js'); // 启动子进程
const numberToCompute = 45; // 一个会导致显著计算耗时的数字
// 监听子进程发回的消息
child.on('message', (message) => {
console.log(`Main process received result from child: Fibonacci(${numberToCompute}) = ${message.result}`);
});
// 向子进程发送任务
console.log(`Main process sending task to child: compute Fibonacci(${numberToCompute}).`);
child.send({ number: numberToCompute });
// 主进程可以继续执行其他任务,不会被计算阻塞
console.log('Main process continues to do other work...');
child.js (子进程)
js
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 监听父进程发来的消息
process.on('message', (message) => {
console.log('Child process received task from parent:', message);
const result = fibonacci(message.number);
// 计算完成后,通过IPC通道将结果发送回父进程
process.send({ result });
// 子进程完成任务后可以自行退出
process.exit();
});
通过这种方式,Node.js应用能够充分利用多核CPU资源,将计算密集型任务 offload 到子进程,同时保持主线程的响应性,以处理高并发的I/O请求。
结语
以上是JS高级篇面试考察点的内容,如有错误欢迎评论区指正。