跳到主要内容前端核心面试题详解:闭包、事件循环、Vue 原理等 | 极客日志JavaScriptNode.js大前端算法
前端核心面试题详解:闭包、事件循环、Vue 原理等
前端面试核心知识点覆盖闭包原理、事件循环机制、BFC 布局、内存泄漏、虚拟 DOM 及 Vue 响应式原理。内容包含 JavaScript 基础、CSS 布局、框架应用、HTTP 协议、性能优化及工程化配置,旨在帮助开发者系统复习前端技术栈并应对面试挑战。
人间失格2 浏览 前端面试题
闭包
1. 定义
**闭包(Closure)**是指一个函数能够访问并记住其外部作用域中的变量,即使外部函数已经执行完毕。闭包由两部分组成:
- 一个函数(通常是内部函数)。
- 该函数被创建时所在的作用域(即外部函数的变量环境)
function outer(){
let count = 0;
function inner(){
count++;
console.log(count);
}
return inner;
}
const counter = outer();
counter();
counter();
2. 闭包的核心原理
- 作用域链:函数在定义时,会记住自己的词法环境。当内部函数访问变量时,会沿着作用域链向上查找。
- 变量持久化:闭包使得外部函数的变量不会被垃圾回收,因为内部函数仍持有对它们的引用。
3. 闭包的常见用途
3.1 私有变量封装
通过闭包隐藏内部变量,仅暴露操作接口:
function createCounter(){
let count = 0;
return {
increment: () => count++,
getValue: () => count
};
}
const counter = createCounter();
counter.increment();
console.log(counter.getValue());
3.2 函数柯里化(Currying)
将多参数函数转换为单参数链式调用:
(){
(){
a + b;
};
}
add5 = ();
.(());
function
add
a
return
function
b
return
const
add
5
console
log
add5
3
3.3 事件处理与回调
function setupButton(){
const button = document.getElementById('myButton');
let clicks = 0;
button.addEventListener('click', function(){
clicks++;
console.log(`按钮被点击了 ${clicks} 次`);
});
}
4. 闭包的陷阱与解决方案
4.1 循环中的闭包问题
问题:循环中创建的闭包共享同一个变量,导致意外结果。
for(var i = 0; i < 3; i++){
setTimeout(function(){
console.log(i);
}, 100);
}
解决方案:使用 IIFE 或 let 创建块级作用域。
for(var i = 0; i < 3; i++){
(function(j){
setTimeout(function(){
console.log(j);
}, 100);
})(i);
}
for(let i = 0; i < 3; i++){
setTimeout(function(){
console.log(i);
}, 100);
}
4.2 内存泄漏
问题:闭包长期持有外部变量引用,导致内存无法释放。
解决方案:在不需要时解除引用。
事件循环(Event Loop)
定义
事件循环是 JavaScript 处理异步任务的核心机制。由于 JavaScript 是单线程语言,事件循环通过任务队列(Task Queue)和调用栈(Call Stack)的协作,实现了非阻塞的异步执行模型。
核心概念
- 调用栈(Call Stack):用于存储函数调用的栈结构,遵循'后进先出'原则。
- 任务队列(Task Queue):用于存储异步任务的回调函数。分为宏任务队列和微任务队列。
- 宏任务(Macro Task)与微任务(Micro Task):
- 宏任务:包括
setTimeout、setInterval、I/O 操作、UI 渲染等。
- 微任务:包括
Promise.then、MutationObserver、process.nextTick(Node.js)等。
事件循环的执行流程
- 同步任务执行:同步任务直接进入调用栈执行,直到调用栈为空。
- 微任务执行:调用栈为空后,事件循环会检查微任务队列,依次执行所有微任务,直到微任务队列为空。
- 宏任务执行:每次从宏任务队列中取出一个任务执行,执行完毕后再次检查微任务队列并执行所有微任务。
- UI 渲染:如果需要进行 UI 渲染,浏览器会在宏任务执行后执行渲染操作。
事件循环示例
console.log('1');
setTimeout(()=>{ console.log('2'); }, 0);
Promise.resolve().then(()=>{ console.log('3'); });
console.log('4');
BFC(块级格式化上下文)
定义
BFC(Block Formatting Context,块级格式化上下文)是 Web 页面渲染时的一种布局环境。它是一个独立的渲染区域,内部的元素布局不会影响外部元素。
触发条件
- 根元素(
<html>)。
float 值不为 none。
position 值为 absolute 或 fixed。
display 值为 inline-block、table-cell、flex、grid 等。
overflow 值不为 visible。
应用场景
- 清除浮动:父元素包含浮动子元素时,通过触发 BFC 解决高度塌陷。
.parent { overflow: hidden; }
- 避免外边距重叠:相邻元素的外边距会重叠,通过触发 BFC 可以避免。
.box { display: inline-block; }
内存泄漏
定义
内存泄漏(Memory Leak)是指程序中已不再使用的内存未被释放,导致内存占用持续增加。在 JavaScript 中,通常由不当的引用管理引起。
常见的内存泄漏场景
- 未释放的缓存或 Map:缓存对象未及时清理。
- DOM 引用未清除:保存了 DOM 元素的引用,即使元素被移除,内存也无法释放。
- 闭包引用:闭包保留对外部作用域的引用,若未释放则相关内存无法回收。
- 未清理的定时器或回调函数:定时器或事件监听器未及时清除。
避免内存泄漏的最佳实践
- 清除定时器、事件监听器、DOM 引用等。
- 使用
WeakMap 或 WeakSet 存储临时数据。
- 避免在闭包中保留不必要的引用。
Vue 的虚拟 DOM
定义
虚拟 DOM(Virtual DOM)是一种用 JavaScript 对象表示真实 DOM 结构的技术。Vue 通过虚拟 DOM 实现高效的 DOM 更新,减少直接操作真实 DOM 的开销。
工作原理
- 生成虚拟 DOM:Vue 将模板编译为渲染函数,生成虚拟 DOM 树。
- Diff 算法:对比新旧虚拟 DOM 树,找出差异,只更新变化的部分。
- 更新真实 DOM:根据 Diff 结果,将变化应用到真实 DOM 上。
MVVM
定义
MVVM(Model-View-ViewModel)是一种软件架构模式,主要用于分离 UI 逻辑与业务逻辑。
- Model:数据模型,负责管理应用程序的数据和业务逻辑。
- View:视图,负责呈现用户界面(UI)。
- ViewModel:视图模型,负责连接 View 和 Model,处理 UI 逻辑和数据绑定。
Vue2 与 Vue3 响应式原理
Vue2 的响应式原理
Vue2 使用 Object.defineProperty 实现响应式,核心机制如下:
- 数据劫持:通过
Object.defineProperty 劫持对象的属性,定义 getter 和 setter。
- 局限性:无法检测新增/删除属性,数组响应式需重写方法。
Vue3 的响应式原理
Vue3 使用 Proxy 实现响应式,核心机制如下:
- 数据代理:通过
Proxy 代理整个对象,拦截对对象的所有操作。
- 优势:支持新增/删除属性、数组索引操作,性能优于
Object.defineProperty。
npm 开发依赖与生产依赖
- 开发依赖(devDependencies):仅在开发环境中使用的依赖包,例如构建工具、测试框架。不会随项目发布到生产环境。
- 生产依赖(dependencies):项目运行所必需的依赖包,例如框架、库。会随项目发布到生产环境。
ES6 模块化
ES6 引入了官方的模块化语法,提供了 import 和 export 关键字来实现模块的导入和导出。
- 静态加载:模块的依赖关系在编译时确定。
- 独立作用域:每个模块拥有独立的作用域,避免全局污染。
Vue 中 key 的作用及为什么不能用 index 作为 key
- key 的作用:唯一标识虚拟 DOM 节点,帮助 Vue 识别哪些节点是新增的、删除的或需要更新的,优化渲染性能。
- 为什么不能用 index:列表顺序变化时,index 会重新分配,导致 Vue 无法正确复用 DOM 节点,状态混乱。
CSS 选择器优先级
CSS 选择器优先级决定了当多个规则应用于同一个元素时,哪条规则会生效。权重从高到低依次为:内联样式 > ID 选择器 > 类选择器 > 元素选择器。
JavaScript 阻塞问题
JavaScript 是单线程语言,长时间运行的同步代码会导致页面卡顿。解决方法包括使用异步 API(如 Promise)、Web Workers、任务拆分等。
Promise 功能与用法
Promise 是 JavaScript 中用于处理异步操作的对象,解决了回调地狱问题。有三种状态:Pending、Fulfilled、Rejected。
前端项目兼容性处理指南
- 浏览器兼容性:使用 Polyfill、CSS 前缀、Babel 等工具。
- 设备兼容性:采用响应式设计、Flexbox 和 Grid 布局。
- 操作系统兼容性:注意字体、文件路径和输入兼容性。
使用 CSS 绘制椭圆的方法
- border-radius:设置
border-radius: 50% 将矩形变为椭圆。
- clip-path:使用
clip-path: ellipse() 裁剪路径。
- SVG:使用 SVG 的
<ellipse> 元素绘制。
setTimeout 和 setInterval
- setTimeout:指定延迟后执行一次回调函数。
- setInterval:每隔指定时间重复执行回调函数。
- setTimeout 设置为 0:回调函数会被放入事件队列,等待当前调用栈清空后再执行。
判断项目性能好不好的方法
核心指标包括加载性能(FCP, LCP)、交互性能(FID, TBT)、视觉稳定性(CLS)。可通过 Lighthouse、Chrome DevTools 等工具检测。
常见的网络请求状态码
HTTP 状态码分为 1xx(信息性)、2xx(成功)、3xx(重定向)、4xx(客户端错误)、5xx(服务器错误)五大类。
箭头函数与普通函数的区别
- this 指向:箭头函数继承外层作用域,普通函数指向调用对象。
- 构造函数:箭头函数不能作为构造函数。
- arguments:箭头函数没有 arguments 对象。
防抖(Debounce)与节流(Throttle)的区别
- 防抖:事件停止触发后执行一次,适合搜索框输入。
- 节流:固定频率执行一次,适合滚动事件。
跨域问题
跨域是由同源策略引起的。解决方案包括 CORS、JSONP、代理服务器、Nginx 反向代理、PostMessage 等。
提高前端项目加载速度的方法
- 优化资源加载:压缩资源、使用 CDN、优化图片。
- 减少请求次数:合并文件、使用雪碧图、HTTP/2。
- 代码优化:代码分割、Tree Shaking、延迟加载。
- 缓存优化:设置浏览器缓存、使用 Service Worker。
页签之间消息传递的方法
- localStorage:监听 storage 事件。
- BroadcastChannel:同一源不同页签通信。
- SharedWorker:后台线程通信。
- postMessage:跨窗口通信。
使元素水平居中的方法
- Flexbox:
justify-content: center。
- Grid:
place-items: center。
- 绝对定位 + transform:
left: 50%; transform: translateX(-50%)。
- margin: 0 auto:适用于固定宽度块级元素。
let、const 和 var 的区别详解
- 作用域:var 是函数作用域,let/const 是块级作用域。
- 提升:var 可提升,let/const 存在暂时性死区。
- 重复声明:var 允许,let/const 不允许。
作用域与作用域链
- 作用域:变量和函数的可访问范围。
- 作用域链:JavaScript 查找变量的机制,由当前作用域和所有父级作用域组成。
JavaScript 继承的几种方式
原型链继承、构造函数继承、组合继承、寄生组合式继承、ES6 类继承等。
前端缓存与数据持久化的理解
- 前端缓存:HTTP 缓存、LocalStorage、Service Worker。
- 数据持久化:LocalStorage、SessionStorage、IndexedDB、Cookies。
require 和 import 的区别详解
- require:CommonJS,动态加载,同步执行。
- import:ES6 Modules,静态加载,异步执行。
px、em 和 rem 的区别详解
- px:绝对单位。
- em:相对单位,基于当前元素字体大小。
- rem:相对单位,基于根元素字体大小。
CSS 盒模型
盒模型由内容区域、内边距、边框和外边距组成。标准盒模型(Content-Box)与 IE 盒模型(Border-Box)的区别在于 width/height 是否包含 padding 和 border。
ES6 新特性
变量声明(let/const)、箭头函数、模板字符串、解构赋值、默认参数、扩展运算符、类、模块化、Promise 等。
HTTP 强缓存与协商缓存
- 强缓存:浏览器直接从本地缓存读取,不发送请求(Cache-Control, Expires)。
- 协商缓存:浏览器发送请求,服务器判断是否使用缓存(Last-Modified, ETag)。
Vue Router 的两种模式
- Hash 模式:URL 包含 #,无需服务器配置,兼容性好。
- History 模式:URL 美观,支持 SEO,需要服务器配置。
JavaScript 的缺点
单线程模型、弱类型语言、浏览器兼容性、安全问题、调试困难等。
JavaScript 原型
每个对象都有原型对象,通过原型链实现属性和方法的继承。
XSS 和 CSRF 详解及防御措施
- XSS:注入恶意脚本。防御:输入过滤、输出编码、CSP。
- CSRF:诱导用户执行非预期操作。防御:验证 Referer、CSRF Token、SameSite Cookie。
HTTP 详解
HTTP 协议的工作流程、请求和响应、方法(GET/POST/PUT/DELETE)、状态码、版本(1.0/1.1/2/3)。
JavaScript 分片(Chunking)
将大数据集或任务分割成多个小块进行处理,常用于优化性能、减少内存占用。
懒加载(Lazy Loading)的原理
延迟加载非关键资源,直到用户需要访问它们时再加载。常用 Intersection Observer API 实现。
脱离文档流的方法
position: absolute/fixed、float、display: none 等。
JavaScript 中堆和栈的存储方式
- 栈:存储基本类型值和函数调用栈。
- 堆:存储引用类型的值。
v-model 原理
v-model 是 v-bind 和 v-on 的语法糖,用于实现双向数据绑定。
Vue 组件通信的多种方式
props/$emit、Event Bus、provide/inject、Vuex、$refs 等。
Diff 算法
比较新旧虚拟 DOM 树的差异,最小化 DOM 操作。策略是同层级比较,通过 key 优化列表项对比。
Webpack 打包配置
入口(Entry)、输出(Output)、加载器(Loader)、插件(Plugin)、模式(Mode)。
浏览器输入 URL 到网页呈现的详细过程
URL 解析、DNS 解析、建立 TCP 连接、发送 HTTP 请求、服务器处理、接收响应、解析渲染页面、执行 JavaScript。
前端的优化方法
减少 HTTP 请求、使用缓存、优化资源加载、优化代码、优化图片、优化渲染性能、优化 SEO。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online