真实小厂前端实习面试经历分享
面试复盘从简历细节到技术深挖我的真实经历分享大家好我是一个大三计算机专业的学生。最近面了几家小厂想和大家聊聊我的看法。这几场面试给我最大的感触就是一定要搞懂自己简历上写的东西包括技术栈掌握的专业技能以及项目经历和实习经历的亮点。今天我就复盘一下我的面试经历希望能帮大家避坑。一、项目背景AI心理助手这个项目是我最新做的一个项目也是写在简历里面最上面的项目这是我跟着b站上博主视频学的大家感兴趣的可以去看一下有做过相关项目的同学也可以看看和自己的有没有区别。我先给大家看一下我在简历上是怎么写这个项目的项目概述AI心理助手是一个结合人工智能技术与心理健康服务的综合性应用核心功能是通过AI对话提供心理支持和情绪疏导。项目专注于为用户提供个性化的心理服务通过情绪分析、会话管理等功能实现智能化的心理健康干预。技术栈Vue 3, Vite, Axios, SCSS项目职责使用Vue 3组合式API构建模块化组件通过Pinia实现状态管理采用sse实现AI回复的流式传输实现AI回复的实时展示优化用户交互体验通过ECharts实现情绪趋势图表直观展示用户情绪变化开发情绪标签展示、风险评估和情绪花园可视化功能通过Vite构建工具和代码分割提升页面加载速度和运行稳定性构建知识库管理、用户管理和数据统计界面支持数据的增删改查组件化开发提高代码复用性和可维护性面试官的问题就从我写的这些细节展开。二、面试问题详解面试官的问题紧密围绕简历下面是我被问到的几个典型问题及我的回答思路。问题1流式对话的实现细节面试官我看你项目里写了流式对话这个具体是怎么实现的我的回答这是项目的核心交互为了让AI回复更有实时对话感并且为了避免用户等待我使用了 Server-Sent Events (SSE) 技术。核心思路是前端发起请求后后端保持连接开放AI每生成一小段文本就推送前端。前端实时更新DOM实现打字机效果。代码层面我们用了 fetch-event-source 库简化操作。以下是代码的简单示例const startAIResponse (sessionId, userMessage) { if (isAiTyping.value) { ElMessage.error(AI助手正在输入中请稍后) return } isAiTyping.value true const aiMessage { id: ai_${Date.now()}_${Math.random().toString(36).substr(2, 9)}, senderType: 2, content: , createAt: new Date().toISOString() } messages.value.push(aiMessage) const ctrl new AbortController() fetchEventSource(/api/psychological-chat/stream, { method: POST, headers: { Content-Type: application/json, Token: localStorage.getItem(token), Accept: text/event-stream }, body: JSON.stringify({ sessionId, userMessage }), signal: ctrl.signal, onopen: (response) { console.log(response) if (response.headers.get(Content-Type) ! text/event-stream) { ElMessage.error(服务器返回非流式数据) } }, onmessage: (event) { const raw event.data.trim() if (!raw) return const eventName event.event const aiMessage messages.value[messages.value.length - 1] if (eventName done) { isAiTyping.value false ctrl.abort() loadSessionEmotion(currentSession.value.sessionId) return } const payload JSON.parse(raw) const ok String(payload.code) 200 if (ok payload.data payload.data.content) { aiMessage.content payload.data.content } else if (!ok) { handleError(payload.message || AI回复失败,请重试) } }, onError: (err) { handleError(err || AI回复失败,请重试) throw err }, onClose: () { loadSessionEmotion(currentSession.value.sessionId) } }) }关键点在回调中实时更新DOM提升用户体验。问题2用户打断处理面试官如果AI回复中用户发送新消息怎么处理我的回答最简单也是最便捷的方法就是直接禁用用户的发送按钮让AI在回答的过程中用户不能再次发送消息。面试官如果不能禁用按钮的话你有什么思路吗我的回答我会考虑两种方案一种是实现消息队列把用户的新问题先存起来等当前AI回复完了再自动处理下一个另一种是提供打断功能用户发新问题时直接中断当前的AI回复开始新的对话这样用户体验会更好不会觉得被卡住。问题3超时处理面试官AI回复中后端超时前端怎么处理我的回答后端超时的话我们会在前端做错误处理。首先捕获错误信息然后给用户一个友好的提示比如AI回复失败请重试。同时把AI消息的内容更新为错误提示重置AI的输入状态让用户可以重新发送消息。这样即使后端超时用户也能知道发生了什么并且可以重新操作。// 错误处理 onError: (err) { console.error(AI 回复失败:, err); ElMessage.error(AI 回复失败请重试); isAiTyping.value false; // 可以实现重试机制 retryCount.value 3 setTimeout(() { startAIResponse(sessionId, userMessage); retryCount.value; }, 1000); }问题4性能优化面试官你再刚刚的自我介绍中说了你会进行页面的优化说说你是怎么做到的我的回答我在项目里主要从这几个方面提升性能首先用Vite作为构建工具它的冷启动和热更新都很快然后组件化开发提高代码复用性合理使用Pinia状态管理避免不必要的渲染流式加载AI回复减少一次性加载的压力还有就是对大组件使用懒加载比如后台管理的一些模块需要时再加载减少初始加载时间。三、总结与心得以上是我面试过程中面试官问到的一小部分问题。大家也可以发现面试官是把八股文融合到项目里面变成场景题来提问。所以简历上的东西一定要掌握实在不会的就不要往上写避免被面试官抓着一个点拷问。在这里我也和大家分享一下我背八股文的一些心得先回答问题本身再延伸到业务或者项目中去。以下常见的一些前端面试题的问题分享希望可以帮到你们ps因为本人技术一般所以有什么问题大家可以指出来避免误导到各位。一、Vue 相关面试题共 12 道问题 1简述一下vue 的生命周期Vue 的生命周期就是一个 Vue 组件从创建、挂载到页面、数据更新再到销毁的整个过程。在这个过程中会自动执行一些钩子函数我们可以在这些时机写自己的业务代码。主要分为四个阶段创建、挂载、更新、销毁对应的钩子是 beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed。created 时组件实例已经创建data 和方法都能用但页面还没渲染出来mounted 时页面已经渲染完成DOM 可以操作了。在业务中我们通常在 created 里面发送接口请求获取数据因为这个时候能拿到 data 且执行时机较早。在 mounted 里操作 DOM、初始化图表、定时器或者第三方插件。在 beforeDestroy 里面清除定时器、解绑事件监听防止内存泄漏保证项目运行更稳定。问题 2双向数据绑定是什么双向数据绑定指的是数据层和视图层之间能够自动同步当数据发生变化时视图会自动更新当用户在视图上修改内容比如输入框打字、选择框切换时对应的数据也会自动改变不需要我们手动去操作 DOM 取值或者赋值。它的核心原理在 Vue 里是通过数据劫持结合发布订阅模式实现的Vue2 用Object.definePropertyVue3 用 Proxy 来监听数据变化从而自动驱动视图更新。在实际工作中双向数据绑定最常用在表单处理场景比如登录页的账号、密码输入搜索框、表单提交等我们只需要用 v-model 绑定数据就能自动完成视图和数据的同步大大减少 DOM 操作代码让开发更简洁高效同时让逻辑更清晰维护起来也更方便。问题 3Vue 组件的通信方式有哪几种Vue 组件通信有许多种我举其中几个例子。第一种是 props 和 $emit这是父子组件最基础、最常用的方式父组件用 props 给子组件传数据子组件用 $emit 触发自定义事件向父组件发送消息适合简单的父子交互。第二种是 ref 父组件给子组件绑定ref就能直接获取子组件实例调用方法或者访问数据一般用在表单校验、手动操作子组件这类场景。第三种是插槽主要用来做内容分发父组件可以把结构或组件传给子组件在封装弹窗、表格、布局组件时特别实用。第四种是useAttrs是vue3里的API用来获取父组件传过来但没在里定义的属性和事件适合二次封装组件、属性透传。第五种是provide和inject用于跨层级通信祖先组件提供数据后代不管层级多深都能直接注入使用避免一层层传。第六种是Vuex和Pinia用来管理全局共享的数据比如用户信息、主题、购物车适合多组件共享状态的大型项目。在业务开发中简单的父子组件交互我们一般用props、ref、插槽跨层级组件通信会用 provide/inject 或 useAttrs全局共享的数据就交给 Pinia 或 Vuex 管理这样能让项目结构更清晰代码更好维护。问题 4为什么 data 属性是一个函数而不是一个对象因为在 Vue 组件里如果 data 是一个对象那所有组件实例都会共用同一个对象一旦一个组件修改了 data其他所有组件都会跟着变就会造成数据混乱。而把 data 写成函数每次创建组件时函数都会返回一个全新的对象这样每个组件实例都有独立的数据互不影响。问题 5动态给 Vue 的 data 添加一个属性时会怎么样怎么解决直接给 Vue data 动态添加未声明的属性这个属性不会具备响应式数据变了页面不会更新因为 Vue 只在初始化时对已存在的属性做响应式处理后续新增的属性不会被自动监听。解决方法主要有两种第一种是使用 this.set第二种是使用 Vue.set。this.set 是 Vue 实例上的方法在组件内部直接使用用来给对象或数组添加响应式属性。Vue.set 是全局方法在非组件环境、工具函数或者单独的 js 文件里使用。在业务中组件内部动态添加属性时我们直接用 this.$set写法简单、最常用如果是在外部 js 模块、工具类里动态添加响应式数据就用 Vue.set。两种方法作用一样只是使用场景不同都能让新增属性变成响应式保证页面正常更新。问题 6v-if 和 v-show 有什么区别使用场景分别是什么v-if 和 v-show 都是用来控制元素显示隐藏的但原理不一样。v-if 是真正的条件渲染表达式为真就渲染为假就直接把元素销毁不渲染到页面上当它初始条件为假时不会渲染性能开销更低但切换时会重建和销毁元素。v-show 不管条件真假都会渲染只是通过 CSS 的 display 属性控制显示和隐藏。初始渲染开销高但切换时只改样式性能更好。v-if 可以配合 v-else 使用也可以放在 template 标签上而v-show 不可以。使用场景上v-if 适合不频繁切换的场景比如根据用户权限控制模块显示因为它不渲染就不会暴露结构安全性更高。v-show 适合频繁切换的场景比如菜单展开收起、下拉框显示隐藏切换更流畅。在业务中权限判断、初始很少变化的区域用 v-if需要频繁点击切换显示隐藏的用 v-show这样既保证性能又提升用户体验。问题 7你知道 Vue 中的 key 的原理吗说说你对它的理解。key 是 Vue 用来识别虚拟 DOM 中每个节点的唯一标识主要帮 Vue 做 Diff 算法优化判断哪些节点是相同的哪些是需要更新的从而尽可能复用 DOM减少不必要的渲染和重建。如果没有 key 或者用了索引 index 当 keyVue 会默认按位置复用节点容易导致数据错乱、渲染不准确。在业务中我们循环列表时一定要用唯一且稳定的值做 key比如 id、唯一编码不要用 index。这样列表在增删改查的时候Vue 能精准定位节点提升渲染性能避免复选框错位、组件状态错乱这类常见 bug保证列表渲染稳定可靠。问题 8说说你对 Vue 中 mixin 的理解有什么应用场景mixin 就是混入是 Vue 里用来实现组件逻辑复用的一种方式我们可以把多个组件共用的 data、methods、生命周期钩子、computed 等单独抽出来组件引入后就会自动合并这些逻辑。一个组件可以引入多个 mixin合并时会按照引入顺序依次合并如果有相同的属性或方法后面引入的 mixin 会覆盖前面的。如果 mixin 和组件本身有同名的选项比如同名方法或钩子组件自身的优先级更高会覆盖 mixin 里的内容。生命周期钩子则不会覆盖而是依次执行先执行 mixin 里的钩子再执行组件自身的钩子。在业务中mixin 常用来抽离公共逻辑比如多个页面共用的列表加载、表单提交、权限判断、埋点统计等可以大幅减少重复代码。但因为它的变量来源不明确、容易命名冲突、可读性差所以现在 Vue3 项目里更推荐用组合式 API 代替 mixin让逻辑更清晰、更易于维护。问题 9Vue 中常见的修饰符有哪些.sync 修饰符的作用是什么Vue 里常见的修饰符主要分为三类分别是事件修饰符、表单修饰符和按键修饰符另外还有 .sync 这种特殊的属性修饰符。事件修饰符用来处理事件行为比如 .stop 阻止冒泡、.prevent 阻止默认行为、.once 只触发一次、.capture 开启捕获模式。表单修饰符用来处理表单输入比如 .lazy 失焦后同步数据、.trim 去掉首尾空格、.number 转数字类型。按键修饰符用来监听特定按键比如 .enter 回车、.esc 取消还有 .ctrl、.shift 这些组合键。.sync 修饰符是 Vue 提供的一个语法糖用来实现属性的双向绑定它的本质就是自动帮我们生成一个 update: 属性名 的事件监听。我们在父组件写 :visible.syncdialogVisible等价于绑定属性加上监听 update:visible 事件子组件只需要通过 $emit 触发这个事件就能直接更新父组件的数据。一个组件可以使用多个修饰符它们会按顺序生效。在业务中事件修饰符可以让我们少写原生事件方法让模板更简洁表单修饰符能减少数据处理的代码提升开发效率.sync 一般用在弹窗、抽屉这类需要父子组件同步状态的场景省去手动写事件和更新的逻辑让代码更简洁、更易维护。问题 10Vue 中的 nextTick 的作用是什么nextTick 的作用是在 DOM 更新完成之后再执行我们指定的回调函数。因为 Vue 更新 DOM 是异步的当我们修改了 data 数据Vue 不会立刻更新页面而是会等同一轮事件循环结束后才更新 DOM这时候如果我们立刻去获取 DOM拿到的还是旧的。使用 nextTick 就能保证我们操作 DOM 时页面已经真正渲染完毕。业务场景1.created/mounted 或者修改数据后想立刻操作 DOM必须用 nextTick 包裹比如获取元素高度、初始化依赖 DOM 的插件。2. 数据更新后获取最新 DOM 状态比如修改列表数据后想获取列表的总高度、滚动位置或者操作更新后的表单元素。3. 配合 v-if 渲染组件后操作子组件比如用 v-if 显示一个子组件后想立刻调用子组件的方法需要在 nextTick 中执行确保子组件已经渲染完成。4. 解决视图更新不及时的问题比如手动修改了数组 / 对象后想确保视图已经更新再做后续操作避免拿到旧的 DOM 状态。问题 11Vue 挂载实例的过程是什么Vue 挂载实例就是把 Vue 实例和真实 DOM 关联起来并把组件渲染到页面上的过程。首先会执行 beforeCreate此时实例刚创建但 data、methods 还没初始化。接着进入 createddata 和方法已经可用但 DOM 还没生成。然后开始编译模板生成虚拟 DOM执行 beforeMount此时模板编译完成但还没挂载到页面。之后 Vue 会把虚拟 DOM 转成真实 DOM替换掉页面上指定的挂载点执行 mounted这时 DOM 渲染完成可以操作节点。挂载完成后数据更新会触发 beforeUpdate 和 updated销毁时执行 beforeDestroy 和 destroyed。问题 12说一下路由传参路由传参就是页面跳转时把数据从一个页面带到另一个页面。主要有三种方式params 传参、query 传参还有 props 解耦。params 是在路由路径里配置占位符比如 /detail/:id通过 name 跳转参数不会显示在地址栏但刷新会丢失query 是拼接在地址后面像id1通过 path 跳转地址能看见刷新不会丢。props 就是把路由参数直接变成组件的 props让组件不用依赖 $route代码更干净。实际工作里不重要、可公开的参数用 query比如列表页跳详情隐私性强、临时用的参数用 params想让组件更通用、更好复用就用 props 接收参数。二、JavaScript 相关面试题共19道问题1说说JavaScript中的数据类型存储上的差别JavaScript的数据类型分为两大类基本数据类型和引用数据类型。基本数据类型有7种分别是undefined、null、boolean、number、string、symbol、bigint引用数据类型主要是object包含普通对象、数组、函数、正则等。存储上的差别是基本类型的值直接存储在栈内存中每个变量都有独立的栈空间赋值时会拷贝一份完整的值修改一个不会影响另一个引用类型的值存储在堆内存中栈内存里只保存指向堆内存的地址赋值时只是拷贝了地址两个变量会指向同一个堆对象修改其中一个会影响另一个。在业务中我们处理简单的变量、参数传递时用基本类型处理复杂的对象、数组数据时用引用类型要注意引用类型赋值带来的引用共享问题避免数据被意外修改。问题2说说你了解的js数据结构JavaScript里常用的数据结构有很多基础的有数组、对象ES6之后新增了Set、Map、WeakSet、WeakMap。数组是有序的集合支持索引访问适合存储有序列表对象是键值对集合适合存储结构化数据。Set是无序不重复的集合用来做数组去重、判断元素存在非常方便Map是键值对集合键可以是任意类型比对象更灵活适合存储需要频繁增删改查的键值数据。WeakSet和WeakMap是弱引用的不会影响垃圾回收适合存储临时的对象引用避免内存泄漏。在业务中数组用来做列表渲染、数据遍历对象用来存储接口返回的结构化数据Set用来去重Map用来做缓存、数据映射根据不同的业务场景选择合适的数据结构能提升代码的性能和可读性。问题3DOM常见的操作有哪些DOM常见的操作主要分为几类第一类是节点查询比如getElementById、querySelector、querySelectorAll这些方法用来获取页面上的DOM元素第二类是节点创建与增删改比如createElement、appendChild、removeChild、replaceChild用来动态创建、添加、删除DOM节点第三类是属性操作比如setAttribute、getAttribute、classList的add/remove/toggle用来修改元素的属性、类名第四类是样式操作比如style属性修改行内样式getComputedStyle获取计算后的样式第五类是事件操作比如addEventListener绑定事件、removeEventListener解绑事件还有节点遍历比如parentNode、children这些属性用来获取父元素、子元素。在业务中我们用DOM操作来实现页面的动态渲染、交互效果比如动态生成列表、绑定点击事件、修改元素样式不过现在Vue、React这类框架会帮我们封装DOM操作我们只需要操作数据框架会自动更新DOM减少手动操作DOM的代码。问题4和区别分别在什么情况使用是抽象相等运算符会自动进行类型转换再比较值是否相等比如1 1会返回true是严格相等运算符不会做类型转换只有类型和值都相等才会返回true1 1会返回false。使用场景上我们日常开发中优先使用避免类型转换带来的意外bug只有在需要做类型转换的比较比如判断null和undefined的时候用 null可以同时判断这两个值其他场景都推荐用保证代码的严谨性。在业务中我们写条件判断、接口数据比较的时候都用只有在特殊的兼容场景才用避免类型转换导致的逻辑错误。问题5typeof与instanceof区别typeof是用来检测基本数据类型的运算符返回一个字符串比如typeof 1返回numbertypeof a returns string不过它有局限性比如typeof null会返回objecttypeof数组、对象都会返回object无法区分具体的引用类型。instanceof是用来检测引用类型的判断一个对象是否属于某个构造函数的实例沿着原型链查找比如[] instanceof Array会返回true{} instanceof Object会返回true它不能检测基本类型。在业务中我们用typeof判断基本类型用instanceof判断引用类型比如判断一个变量是不是数组用Array.isArray比instanceof更准确避免跨iframe的原型问题。问题6JavaScript原型原型链有什么特点每个JavaScript函数都有一个prototype属性也就是原型对象实例对象的__proto__属性会指向构造函数的prototype原型对象里可以定义共享的方法和属性所有实例都能访问。原型链就是实例对象的__proto__指向构造函数的prototype而原型对象的__proto__又指向父类的原型一直到Object.prototype的__proto__为null形成的链式结构就是原型链JS就是通过原型链来实现继承的。原型链的特点是查找属性和方法的时候会从实例本身开始沿着原型链向上查找直到找到或者到null为止上层原型的属性和方法可以被所有下层实例访问。在业务中我们用原型来给构造函数添加共享方法减少内存占用用原型链来实现继承现在ES6的class语法糖本质也是基于原型实现的原型是JS面向对象的核心基础。问题7说说你对作用域链的理解作用域就是变量和函数的可访问范围分为全局作用域、函数作用域、块级作用域ES6的let/const。作用域链是当代码执行时查找变量的顺序从当前作用域开始向上一级作用域查找一直到全局作用域形成的链式结构就是作用域链。作用域链的作用是保证变量的有序访问内层作用域可以访问外层作用域的变量外层不能访问内层的变量避免变量污染。在业务中我们用作用域链来理解变量的查找规则避免变量提升、闭包带来的变量访问问题写代码时合理利用作用域封装私有变量避免全局变量污染。问题8谈谈this对象的理解this是JavaScript中的一个关键字它的指向不是固定的是在函数执行时确定的不同的调用场景指向不同。全局环境下this指向window浏览器中普通函数调用this指向window严格模式下是undefined对象方法调用this指向调用该方法的对象构造函数调用this指向新创建的实例箭头函数没有自己的this继承外层作用域的thiscall、apply、bind可以手动改变this的指向。在业务中我们要明确this的指向避免this指向错误导致的bug比如在定时器、事件回调中用箭头函数或者bind绑定this保证this指向正确的组件实例在Vue、React的组件方法中this的指向是核心直接影响组件的逻辑正确性。问题9说说new操作符具体干了什么new操作符用来创建构造函数的实例具体做了四件事第一创建一个全新的空对象第二把这个空对象的__proto__指向构造函数的prototype让实例可以访问原型上的方法第三把构造函数的this指向这个新对象执行构造函数的代码给实例添加属性和方法第四如果构造函数没有返回对象就返回这个新创建的实例如果返回了对象就返回这个对象。在业务中我们用new来创建自定义对象的实例理解new的执行过程能更好地理解JS的面向对象和原型机制手写new方法也是前端面试的常见考点。问题10bind、call、apply区别?如何实现一个bind?call、apply、bind都是用来改变函数的this指向的区别是call和apply会立即执行函数call接收参数列表apply接收参数数组bind不会立即执行会返回一个新的函数新函数的this被永久绑定为指定的对象参数可以分两次传入。实现bind的话核心是利用apply在返回的新函数里把this绑定到指定的对象同时处理柯里化的参数还要处理new调用的情况保证new的优先级高于bind的绑定。在业务中我们用call、apply来改变this指向比如借用数组方法处理类数组对象用bind来绑定函数的this比如在事件回调、定时器中绑定组件的this避免this指向错误。问题11JavaScript中执行上下文和执行栈是什么执行上下文是JS代码执行时的环境包含变量对象、作用域链、this指向分为全局执行上下文、函数执行上下文、eval执行上下文。执行栈是用来管理执行上下文的栈结构遵循先进后出的原则代码执行时先创建全局执行上下文入栈遇到函数调用就创建函数执行上下文入栈函数执行完就出栈最后全局执行上下文出栈。执行上下文的创建分为两个阶段创建阶段生成变量对象、建立作用域链、确定this指向执行阶段变量赋值、执行代码。在业务中理解执行上下文和执行栈能帮我们理解变量提升、this指向、异步执行的原理解决代码执行顺序的问题比如异步任务会进入任务队列等执行栈空了再执行。问题12说说JavaScript中的事件模型JavaScript的事件模型分为三个阶段事件捕获阶段、目标阶段、事件冒泡阶段。事件捕获是从最外层的window到目标元素从上到下传播目标阶段是到达触发事件的目标元素事件冒泡是从目标元素回到window从下到上传播。我们可以用addEventListener的第三个参数来控制true表示在捕获阶段触发false默认表示在冒泡阶段触发。事件模型的核心是事件流我们可以用stopPropagation阻止事件冒泡用preventDefault阻止默认行为。在业务中我们用事件模型来理解事件的传播顺序实现事件代理阻止不必要的事件冒泡避免事件冲突。问题13解释下什么是事件代理?应用场景?事件代理也叫事件委托是利用事件冒泡机制把事件统一绑定在父元素上而不是给每个子元素单独绑定事件。当子元素触发事件时事件会冒泡到父元素再通过 event.target 判断具体触发的子元素执行对应的逻辑。它的优点是减少事件绑定数量提升性能并且可以自动处理动态新增的 DOM 元素不需要重新绑定事件。在业务中事件代理常用于长列表、表格操作按钮、动态生成的菜单等场景比如商品列表的点击事件只需要给父容器绑定一次就能处理所有子项的交互。问题14说说你对闭包的理解?闭包使用场景闭包是指函数嵌套函数时内层函数可以访问并持有外层函数作用域里的变量即使外层函数已经执行完毕其作用域也不会被销毁。闭包的核心是保留作用域实现变量私有化和持久化。常见使用场景包括模块化开发、封装私有变量、实现防抖和节流、柯里化函数、保存定时器或回调函数状态等。Vue 组件内部的响应式状态和 Composition API 也大量依赖闭包机制。在业务中闭包可以避免全局变量污染实现独立的功能模块。但同时要注意闭包会保留引用可能造成内存泄漏不需要时要及时解除引用。问题15谈谈JavaScript中的类型转换机制JavaScript 类型转换分为显式类型转换和隐式类型转换。第一类是显式转换由开发者手动调用方法完成比如 Number()、String()、Boolean()、parseInt 等转换行为明确可控。第二类是隐式转换是 JavaScript 在运算、比较、条件判断时自动执行的类型转换例如字符串和数字相加、 比较、if 判断等场景都会触发。常见规则数字与字符串相加会转为字符串判断时 0、、null、undefined、NaN 会被转为 false 比较时会优先转为数字再比较。在业务中应尽量避免复杂隐式转换多使用 严格相等减少意外 bug保证逻辑稳定可靠。问题16深拷贝浅拷贝的区别?如何实现一个深拷贝?浅拷贝只复制对象的第一层属性对于引用类型的属性只复制地址新旧对象会共享同一块堆内存修改一个会影响另一个。深拷贝会递归复制对象所有层级生成完全独立的新对象新旧对象互不影响。实现方式第一类是浅拷贝常用 Object.assign、展开运算符 ...、数组的 slice、concat 等。第二类是深拷贝简单场景可用 JSON.parse(JSON.stringify())但无法处理函数、Symbol、正则、日期和循环引用复杂场景需要手写递归遍历对对象、数组、特殊类型逐一拷贝并处理循环引用问题。在业务中涉及表单数据备份、状态重置、接口数据缓存时通常使用深拷贝避免原数据被意外修改。问题17数组的常用方法有哪些数组常用方法可以分为几类第一类是遍历处理方法如 forEach、map、filter、reduce、some、every用于遍历、筛选、计算、数据转换。第二类是增删改方法如 push、pop、shift、unshift、splice用于在数组头尾或指定位置增删元素。第三类是查找方法如 indexOf、includes、find、findIndex用于查找元素是否存在或获取位置。第四类是拼接与转换方法如 slice、concat、join、sort、reverse用于截取、合并、排序、转字符串等。在业务中map 常用于列表数据渲染filter 用于筛选数据reduce 用于统计求和splice 用于动态修改数组是最常用的核心方法。问题18说说Promise 以及他们的钩子函数Promise 是 JavaScript 解决异步编程的方案用来避免回调地狱让异步逻辑更清晰。它有三种状态pending 进行中、fulfilled 成功、rejected 失败状态一旦改变就不会再变化。核心钩子函数第一类是 then在异步操作成功时执行接收 resolve 传递的数据。第二类是 catch在异步操作失败时执行接收 reject 抛出的错误。第三类是 finally无论成功或失败都会执行常用于关闭加载状态、清理资源。在业务中Promise 主要用于处理接口请求、定时器、文件读取等异步逻辑配合 async/await 可以让代码更接近同步写法。问题19说说Promise.allPromise.all 可以批量执行多个 Promise 异步任务接收一个 Promise 数组等待所有任务完成后统一返回结果。特点第一类是全部成功才会进入 then返回结果数组顺序与传入顺序一致。第二类是只要有一个失败就会立即进入 catch不会等待其他任务完成。在业务中常用于页面同时请求多个不相关接口合并等待时间提升页面加载速度例如首页同时获取用户信息、轮播图、推荐列表等。三、CSS 相关面试题共5道问题1行内元素和块级元素区别是什么块级元素独占一行可设置宽高、内外边距默认宽度100%比如div、p、h1、ul、section行内元素不换行宽高由内容决定不能设置宽高、上下边距比如span、a、i、input行内块元素不换行可设置宽高比如img、button。开发中根据布局需求选择语义化布局优先用块级语义标签。问题2平时开发怎么理解语义化语义化就是用正确的标签做正确的事让页面结构更清晰比如用header做头部、nav做导航、main做主体、section做区域、footer做底部而不是全部用div。好处1. 代码可读性高方便团队维护2. 利于SEO搜索引擎抓取3. 适配屏幕阅读器提升可访问性4. 页面结构清晰便于样式开发。问题3三列布局 要让旁边两列固定中间自适应怎么做最常用、最简单的是Flex布局父元素设置display:flex左右两个子元素固定宽度中间子元素设置flex:1自动占满剩余宽度。其他方案浮动布局需清除浮动、绝对定位脱离文档流但Flex兼容性好、代码简洁是首选方案。问题4居中有几种方式实现1. 水平居中行内元素用text-align:center块级元素用margin:0 auto2. 垂直居中单行文本用line-height高度3. 通用居中最常用Flex布局父元素display:flex; justify-content:center; align-items:center4. 绝对定位居中position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);5. 表格居中display:table-cell; vertical-align:middle;开发中优先用Flex居中简洁通用适配绝大多数场景。问题5rem和vw你是怎么用的两者都是移动端适配单位rem相对单位基准是html根节点的字体大小通过动态设置根节点font-size实现不同屏幕适配vw视口单位1vw等于视口宽度的1%直接基于屏幕宽度计算无需计算根节点使用场景移动端项目用vw更简单PC移动端适配用rem配合媒体查询。四、浏览器 工程化 综合面试题共9道问题1说说你对BOM的理解常见的BOM对象你了解哪些BOM就是浏览器对象模型是用来操作浏览器窗口的API它是JS和浏览器交互的桥梁核心是window对象所有的全局变量、函数都是window对象的属性和方法。常见的BOM对象有window对象代表浏览器窗口提供了定时器、弹窗等方法location对象用来操作页面的URL比如获取地址、跳转页面history对象用来操作浏览器的历史记录比如前进、后退navigator对象用来获取浏览器的信息比如用户代理、设备信息screen对象用来获取屏幕的尺寸、分辨率。在业务中我们用location做页面跳转、获取URL参数用history做路由的前进后退用navigator判断设备类型、做浏览器兼容性处理BOM是前端和浏览器交互的基础很多页面的交互、路由功能都依赖BOM对象。问题2页面性能怎么提升页面性能优化我会从加载、渲染、运行三个维度入手加载阶段用Vite实现路由懒加载、代码分割减少首屏包体积图片压缩懒加载静态资源CDN加速开启gzip压缩。渲染阶段避免不必要的重绘重排长列表用虚拟滚动Vue中用v-memo、v-once缓存静态内容合理使用key。运行阶段防抖节流处理高频事件滚动、输入及时清除定时器、事件监听防止内存泄漏优化接口请求合并接口、做接口缓存。问题3分割代码你是怎么实现的代码分割就是把项目打包后的大文件拆分成多个小文件按需加载减少首屏加载时间。我在Vue3项目中主要用两种方式一是路由懒加载通过import()语法让每个路由单独打包用户访问对应页面才加载该文件二是组件异步加载用defineAsyncComponent懒加载非首屏组件。Vite会自动识别这些语法打包时拆分代码结合路由守卫实现按需加载。问题4浏览器缓存怎么实现的浏览器缓存分为强缓存和协商缓存前端主要做配置和业务层缓存强缓存通过Cache-Control、Expires实现资源直接从本地读取不请求服务器协商缓存通过ETag、Last-Modified资源过期后和服务器校验未变化返回304使用缓存。业务开发中我还会用localStorage/sessionStorage缓存静态数据、用户信息用axios拦截器做接口数据缓存避免重复请求。问题5登录实现的思路登录是前端基础功能我的实现思路分5步第一步表单校验验证账号、密码格式合法性第二步调用登录接口传递账号密码后端校验后返回token第三步前端把token存储到localStorage/Pinia方便全局使用第四步通过axios请求拦截器统一在请求头携带token第五步登录成功后跳转首页同时请求用户信息存储到全局状态管理额外做了权限控制路由守卫校验token未登录自动跳转登录页已登录禁止回跳登录页。问题6token过期的话怎么办token过期后端会返回401状态码我做了无感刷新兜底处理首先通过axios响应拦截器监听401错误然后自动调用刷新token接口获取新token后重新存储再自动重新发送刚才失败的请求用户无感知提升体验如果刷新token也过期登录失效就清除本地所有缓存强制跳转登录页提示用户重新登录。问题7token 和cookie区别1. 存储位置token存在localStorage/sessionStoragecookie存在浏览器本地自动携带在请求头2. 大小限制token无固定大小限制cookie最大4KB3. 安全性token可加密手动携带防CSRFcookie易受CSRF攻击4. 跨域token支持跨域cookie默认跨域受限5. 使用场景token用于登录鉴权、接口校验cookie用于存储少量会话信息。问题8localeStorage、seesionStoage 还有cookie区别三者都是浏览器本地存储核心区别3点1. 生命周期localStorage永久存储手动清除sessionStorage会话存储关闭页面失效cookie可设置过期时间2. 存储大小localStorage/sessionStorage约5MBcookie约4KB3. 与服务端通信cookie自动携带在请求头前两者不参与纯前端存储业务使用localStorage存token、用户配置sessionStorage存临时会话数据cookie仅用于兼容老项目不存重要信息。问题9说一下axios拦截器是什么axios拦截器分为请求拦截器和响应拦截器是前端接口处理的核心请求拦截器在发送请求前执行主要做携带token、加载动画、请求参数处理响应拦截器在接收响应后执行主要做关闭加载、统一处理数据、错误捕获、token过期刷新好处是不用每个接口单独写逻辑全局统一处理减少重复代码提升开发效率。以上就是常见的面试题总结谢谢大家