十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

前端经典面试题

前端经典面试题 一、JSvar let const 区别for(vari0;i3;i){setTimeout((){console.log(i);},1000);}输出333for循环是同步的它会一口气把3个setTimeout全注册完此时i已经变成3了。等100ms后主线程空闲了回调才开始执行这时去读i读到的自然是3。原因即使延迟是0setTimeout 的回调依然会被放到任务队列必须等主线程的 for 循环全部执行完毕同步代码才会被执行。所以读到的 i 依然是3var 没有块级作用域整个循环共用同一个 ijs数据类型 typeof基本类型 string number boolean null undefined symbol bigint引用类型 Object 、Array 、Function 、Data Map / Set RegExp经典案例leta100;letba;a200;console.log(a);//200console.log(b);//100letobj1{name:st};letobj2obj1;obj1.namepp;console.log(obj1.name);//ppconsole.log(obj2.name);//ppconsole.log({}{})//false 两个独立的引用地址– typeof陷阱typeof null //object null指针是(0*00) 对象的类型标签恰好是0 改的话涉及的比较多解决 typeof [] typeof{} //object 无法分别具体的对象类型 [] {}都是object解决 Array.isArray()– 终极解决方案Object.prototype.toString.call()最精确判断所有类型instanceofconsole.log({} instanceof Object); // true 判断对象是否为某构造函数的实例 对基本类型无效堆栈/内存堆Heap和栈Stack是内存中的两种存储区域**栈内存Stack**存储基本数据类型Number、String、Boolean等和引用数据类型的内存地址指针。它的特点是“先进后出、大小固定、自动释放”。堆内存Heap存储引用数据类型Object、Array、Function等的实际数据。它的特点是“无序存储、大小动态、需要垃圾回收GC”。先进先出基本类型赋值是“复制值”互不影响引用类型赋值是“复制地址”会相互影响递归太多会栈溢出闭包/定时器没清理会堆内存泄漏。this call/apply/bind“this 指向函数调用时的上下文不是定义时。谁调用我我指向谁。箭头函数例外它指向定义时的外层作用域。Vue2 的 methods 中 this 指向组件实例不能用箭头函数。”追问1call、apply、bind 的区别“三者都是手动改变 this 指向。call 和 apply 立即执行区别是传参方式不同call 逐个传apply 数组传。bind 返回新函数不立即执行固定this指向适合需要绑定的回调函数如定时器。”追问2箭头函数和普通函数 this 的区别“箭头函数 this 是定义时继承外层作用域不能通过 call/apply/bind 改变普通函数 this 是调用时动态绑定可以通过 call/apply/bind 改变。箭头函数没有 arguments不能 new。”追问3setTimeout 里 this 为什么指向 windowsetTimeout 的回调是 setTimeout 内部通过普通函数调用的方式执行的而普通函数调用时 this 默认指向全局对象 window追问4Vue3 组合式 API 中 this 怎么用“Vue3 的 setup 中没有 this响应式数据用 ref/reactive 声明直接使用变量名不用 this。生命周期钩子也一样。”闭包使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染缺点是闭包会常驻内存会增大内存使用量使用不当很容易造成内存泄露。闭包有三个特性1.函数嵌套函数2.函数内部可以引用外部的参数和变量3.参数和变量不会被垃圾回收机制回收**优点:**可以创建私有变量、数据封装、实现柯里化等高级函数式编程模式。**潜在问题:**主要是内存泄漏(因为外部变量无法被回收)和循环中的变量引用问题。**避免内存泄漏的方法:**在不需要时手动解除引用(null)以及及时移除事件监听器。柯里化/高阶函数柯里化: 将多参数函数转化为一系列只接收单一参数的函数高阶函数参数或返回值为函数的函数 ① 参数是函数如 map、filter、setTimeout ② 返回值是函数如 bind、debounce柯里化应用场景: 延迟执行 复用性 组合性递归“递归是函数自己调用自己的一种编程技巧适合处理重复性、层级未知的问题比如树形结构遍历、深拷贝、菜单渲染等。”// 阶乘functionfactorial(n){if(n1)return1// ✅ 终止条件n 减小到 1 时停止returnn*factorial(n-1)// 递归体自己调自己n 逐渐减小}factorial(5)// 120拷贝“浅拷贝拷贝的是引用地址深拷贝拷贝的是所有数据。浅拷贝新旧对象共用同一块内存深拷贝新旧对象完全独立。”1、浅拷贝浅拷贝只复制某个对象的引用而不复制对象本身新旧对象还是共享同一块内存浅拷贝只复制一层属性会污染二层的数据constobj{name:张三,info:{age:18}}// 1. 扩展运算符constcopy1{...obj}// 2. Object.assign() 合并对象constcopy2Object.assign({},obj)// 3. 数组的 slice() / concat()constarr[1,2,[3,4]]constcopy3arr.slice()// 4. 手写循环constcopy4{}for(letkeyinobj){copy4[key]obj[key]}2、深拷贝深拷贝会创造一个一摸一样的对象新对象和原对象不共享内存修改新对象不会改变原对对象。constobj{name:张三,info:{age:18,hobbies:[游泳,跑步]}}// 方式1JSON 序列化最常用但有局限constcopy1JSON.parse(JSON.stringify(obj))// ✅ 缺点无法拷贝函数、undefined、Symbol、循环引用// 方式2 首选constcopy1structuredClone(obj)// ✅ 缺点无法拷贝函数、Error对象 DOM节点// 方式3lodash.cloneDeep()// 方式4手写递归面试常考functiondeepClone(obj){// 非对象直接返回if(objnull||typeofobj!object)returnobjconstresultArray.isArray(obj)?[]:{}for(letkeyinobj){// 只拷贝自身属性if(obj.hasOwnProperty(key)){result[key]deepClone(obj[key])// 递归拷贝}}returnresult}constcopy2deepClone(obj)[]![]//true↓[]false// 先算 ![] → false↓[]0// false 转数字 → 0↓0// [] 转原始值 → 走toString()↓00// 转数字 → 0↓truelocalStorange sessionStorange cookiefor循环 数组方法 字符串方法 对象方法foreach 没有返回值 不能链式调用map 返回一个新数组 可以链式调用(因为返回的新数组)for(var item of arr){ 不能遍历对象 遍历出来就是每一项for(var item in arr){ 可以遍历对象 遍历出来是对象的键名数组map foreach filter find findIndex some every includes reduce(累加器初始值) push pop shift unshift splice slice join reverse tostring concat返回新数组map filter concat slice字符串split trim slice indexof substring match search匹配正则 replace toUpperCase toLowerCase startsWith(是否以某字符开头) endsWith对象Object.keys(obj) Object.values(obj) Object.entries(obj)对象转为数组格式回流和重绘回流当元素的几何属性宽高、位置、边距或布局结构显示隐藏、DOM 增删、文字变化发生改变时浏览器需要重新计算元素的位置和大小重新构建布局树这个过程叫回流。**重绘**当元素的外观风格颜色、背景、边框、阴影发生改变但几何尺寸和布局不变时浏览器只需要重新绘制像素不需要重新计算布局这个过程叫重绘。“回流和重绘是浏览器渲染页面的两个关键步骤。回流是重新计算布局重绘是重新绘制外观。回流一定触发重绘重绘不一定触发回流。”跨域“跨域是浏览器的同源策略导致的解决跨域的核心思路就是让浏览器认为请求是合法的或者绕过浏览器的限制。主要分开发环境和生产环境两个场景。”浏览器同源策略协议 域名 端口 三者必须完全一致任何一个不同就是跨域。代理跨域“开发环境用 Vite/Webpack proxy 代理配置 changeOrigin: true 转发到后端。生产环境主流方案是 CORS后端配 Access-Control-Allow-Origin 响应头或者 Nginx 反向代理让前端和接口同源。JSONP 只支持 GET已经淘汰了。非简单请求会先发 OPTIONS 预检询问后端是否支持。”解绑事件btn1.onclick nullbtn2.removeEventListener(‘click’, showMsg, false);vue2 $offVue3 移除了on/on/on/off推荐用 mitt函数防抖debounce在事件被触发n秒后再执行回调如果在这n秒内又被触发则重新计时。场景直播点赞、输入框搜索建议 resize函数节流throttle**规定在一个单位时间内只能触发一次函数。如果这个单位时间内触发多次函数只有一次生效。场景窗口resize scroll事件宏微任务先微后宏1.宏任务分类setTimeout setInterval setImmediate2.微任务分类new promise().then(回调)process.nextTick(独立的队列 优先级高于微任务)先同步再微任务后宏任务。每一轮执行一个宏任务 → 执行完立即处理当前所有微任务 → 再取下一个宏任务如此循环。event loop循环机制“Event Loop 是 JS 的异步执行机制。JS 是单线程遇到异步任务会放到任务队列。执行顺序先执行所有同步代码然后清空所有微任务再取一个宏任务执行执行完再清微任务如此循环。微任务包括 Promise.then、process.nextTick宏任务包括 setTimeout、setInterval、事件回调。微任务优先级高于宏任务。”diffDiff 是虚拟 DOM 的核心算法用来对比新旧两棵虚拟 DOM 树的差异“Diff 算法对比新旧虚拟 DOM 树找出差异只更新变化的部分减少 DOM 操作。Vue3 比 Vue2 多了静态标记和块树优化编译时就标记动态节点Diff 时跳过静态内容性能提升 2 倍。key 用唯一 id 而不是 index帮助 Diff 正确复用节点。”虚拟DOM“虚拟 DOM 是用 JS 对象模拟真实 DOM 的树形结构。数据变化时生成新虚拟 DOM通过 Diff 算法对比新旧树找出差异再批量更新到真实 DOM减少直接操作 DOM 的昂贵开销。虚拟 DOM 的优点是减少 DOM 操作、跨平台、数据驱动视图缺点是首次渲染比直接操作 DOM 慢需要额外的 Diff 开销。Vue3 通过静态标记和块树优化了虚拟 DOM只比对动态节点比 Vue2 快约 2 倍。”route router的区别“router 是VueRouter的一个对象route 是当前路由状态信息。 简单说router 负责跳转route 负责读取。”$router.push({path:‘home’})$router.replace({path:‘home’});route为当前router跳转对象里面可以获取name、path、query、params等router为VueRouter实例想要导航到不同URL则使用router.push方法get post的区别二、vueref和reactive的区别二者都能创建响应式数据ref() 用于基本类型访问.valuereactive() 用于对象/数组类型直接访问会深层响应式Vue2 vs Vue3 核心区别面试速记版双向绑定的原理Vue 通过Object.defineProperty()来劫持各个属性的 setter / getter每一个被劫持的属性都会生成 get 和 set 拦截器。它采用数据劫持结合观察者模式的方式在数据变动时通过 setter 发布消息给订阅者触发相应的监听回调从而驱动视图更新。“Vue3 这块把defineProperty 换成了 Proxy不需要再遍历每个属性了直接代理整个对象。”watch监听deep: true—— 深度监听可以监听到对象或数组内部值的变化默认情况下不开启 deepwatch 只能监听简单类型可以直接监听到值的变化但对于对象/数组修改内部属性不会触发监听immediate: true—— 立即执行一次监听回调默认第一次不会执行watch和computed的区别使用场景computed----当一个属性受多个属性影响的时候使用computed-----购物车商品结算。watch–当一条数据影响多条数据的时候使用watch-----搜索框.MVC和MVVM的区别“MVC 是数据变了要手动操作 DOM 去更新视图开发者既要管数据又要管页面。MVVM 通过 ViewModel 实现了双向绑定数据变视图自动变视图操作数据也自动变开发者只关心数据DOM 操作全部交给框架。Vue 就是典型的 MVVM 思想。”webpack和vite的区别项目构建打包工具精简版Webpack 是全部打包好再给你Vite 是你要什么我现给你编译。 新项目追求开发体验选 Vite老项目/复杂工程化需求选 Webpack。vuex和pinia的区别“Pinia 是 Vuex 的升级版Vuex 4 是 Vue3 的适配版但 Pinia 是专为 Vue3 设计的状态管理库官方现在也推荐用 Pinia。”“Vuex 有 state/getters/mutations/actions 四个核心修改数据要走 commit mutations 或 dispatch actionsTS 支持一般。Pinia 去掉了 mutations只有 state/getters/actions数据直接改就行TS 原生支持代码量少 30%。跨模块调用直接 import 其他 Store还有类型提示。逻辑复用可以封装组合式函数比 Vuex 的 mixins 好用。新项目官方推荐 Pinia。”使用场景用户登录信息token、用户资料、权限标识动态路由权限控制根据角色生成菜单/路由表全局UI主题配置暗黑模式、多语言、侧边栏折叠状态多步骤表单数据暂存分步提交场景下的跨组件数据共享购物车数据商品数量、选中状态跨页面共享全局消息/通知数量未读红点WebSocket推送更新和全局变量的区别vuex的状态存储是响应式的当Vue组件从store中读取状态的时候若store中的状态发生变化那么相应的组件也会得到高效更新。全局变量可能操作命名污染但是vuex不会每个组件可以根据自己vuex的变量名引用不受影响监听vuex中数据变化mixins和hooks的区别“mixin 和 Hook 都是逻辑复用的方式mixin 是 Vue2 的方案Hook组合式函数是 Vue3 的方案。Hook 是 mixin 的升级替代解决了 mixin 的所有痛点。”精简版“mixin 是 Vue2 的逻辑复用方式但问题很多数据来源不明、命名冲突、隐式依赖、TS 支持差。Hook 是 Vue3 的组合式函数通过显式 import 和解构数据来源清晰、重名可控、TS 原生支持。Vue3 官方推荐用 Hook 替代 mixin新项目不要再写 mixin 了。”如果组件里有重名精简版“Hook 如果和本组件方法同名解构时重命名就行const { add: addCount } useCounter()互不影响。如果重名不处理Vue 会直接报错逼你解决。而 Mixin 同名是静默覆盖不报错你根本不知道出了问题调试非常痛苦。这也是 Hook 比 Mixin 更好的原因之一。”ES6Array.from 将对象或者字符串转为数组includes()表示某个数组是否包含给定的值“async/await 是 Promise 的语法糖让异步代码像同步一样写错误处理用 try/catch。底层还是 Promise。”精简版“ES6 新增了 let/const、箭头函数、解构、模板字符串、扩展运算符、Class、模块化 import/export、Promise、async/await、Map/Set 等。Vue3 源码和日常开发都在用比如 setup 里解构 props、import 组件、async/await 发请求。最关键的是模块化和箭头函数基本天天用。”Promise“Promise 是 ES6 新增的异步编程解决方案用来解决传统的回调地狱问题。它相当于一个’承诺’表示一个异步操作将来会返回成功或失败的结果。”“Promise 是 ES6 的异步解决方案有三个状态进行中Pending、已成功Fulfilled和已失败Rejected状态一旦改变不可逆。常用 then 处理成功、catch 捕获失败、finally 收尾。Promise.all 并行请求所有接口都成功才返回Promise.race 谁先完成返回谁。解决了回调地狱问题async/await 是它的语法糖让异步代码像同步写。”父子组件执行顺序父组件beforeCreated -父组件created -父组件beforeMounted -子组件beforeCreated -子组件created -子组件beforeMounted -子组件mounted - 父组件mountedkeep-alive可以在组件切换时 缓存的作用include - 字符串或正则表达只有匹配的组件会被缓存exclude - 字符串或正则表达式任何匹配的组件都不会被缓存生命周期beforeCreate() 创建前 还未进行数据代理created() 已完成初始化beforeMount 初始化dom 还不能用mounted dom初始化完成beforeUpdate 数据页面更新前updated 此时数据和页面都以更新beforeDestroy 实例销毁之前调用destroyed 实例销毁之后ref获取dom元素获取子组件中的data 调用子组件的方法HelloWorld refhello/ this.$refs.hello“Vue2 的 ref 是 DOM 引用用 this.$refs 获取元素或组件实例。Vue3 的 ref 是响应式 API声明响应式数据用 ref(0)访问/修改要 .value模板中自动解包。ref 也保留了获取 DOM 和组件实例的能力。和 reactive 比ref 支持任意类型、可以整体重新赋值、解构不丢响应式所以我习惯优先用 ref。”事件修饰符.stop阻止冒泡.prevent阻止默认事件的发生.capture捕获冒泡.self将事件绑定到自身.once设置事件只能触发一次.sync就是一个更新数据 update的缩写三、AI相关SOP标准作业程序使用场景基于AI前后端开发流程MCP (模型上下文协议)一个开放协议定义了AI应用与外部数据源、工具之间标准化的连接方式。使用场景• 连接数据库执行sql• 操作Git自动生成commit信息、创建分支、提交PR。• 连接设计工具从Figma API直接拉取设计稿数据生成代码骨架。Skills技能包封装好的技能包把一组指令、约束、工作流程打包成一个可重复使用的标准化模块。使用场景• 组件库开发规范新建组件时的目录结构、命名、测试用例编写流程。• 项目发布流程从打包、测试到部署到不同环境的操作步骤。• “创建新页面”自动按照项目规范生成页面并在路由配置里注册。• “代码审查”按照团队Code Review Checklist命名规范、边界处理、性能隐患逐项检查你的PR代码。• “生成API接口调用”根据后端apifox文档自动生成TS类型定义和请求函数。RAG (检索增强生成)AI的本地知识库让AI在回答前先从指定的知识库中检索相关资料再结合这些资料生成答案。使用场景• 查阅内部组件库文档让AI准确使用公司自研的Button/Table组件API。• 检索历史故障案例遇到报错时先查团队Wiki里的踩坑记录。• 读取项目架构决策记录ADR 让AI理解为什么项目选择了某种技术方案。前四、综合问题封装的方法商品 订单 封装请求拦截、金额 / 日期等通用工具、支付与埋点工具二次封装业务通用组件 tab 轮播图 购物车 倒计时 弹框包括二次封装uView里面的组件弹框 轮播图 预览图片 分页 搜索性能优化图片放在oss 懒加载减少http请求路由懒加载、分包UI 按需引入大列表用虚拟列表图片懒加载压缩代码方面封装组件 封装方法 减少对DOM的操作 按需加载并发减少请求数量利用缓存减少一些不必要的请求节流、防抖 提交锁项目中遇到难解决的问题vod相关兼容问题五、其它浏览器的执行过程“从输入URL到页面展示核心是 DNS解析 → TCP连接 → 请求响应 → 解析渲染 四个阶段。”“浏览器执行过程分四步① DNS解析把域名转成IP② 建立TCP连接三次握手③ 发送HTTP请求服务端返回资源HTML、CSS、JS等④ 浏览器解析渲染解析HTML构建DOM树解析CSS构建CSSOM树两者合并成渲染树然后布局回流、绘制重绘、合成显示。遇到JS会阻塞解析defer/async可以控制加载时机。”浏览器的缓存机制“浏览器缓存分为强缓存和协商缓存核心是减少重复请求提升加载速度。”“浏览器缓存分两种强缓存和协商缓存。强缓存用 Cache-Control优先级最高和 Expires 控制命中后直接读缓存不走服务器。协商缓存用 Last-Modified / If-Modified-Since 和 ETag / If-None-Match 控制会向服务器发请求验证资源没变就返回304变就返回200和新资源。ETag 比 Last-Modified 更精准基于文件内容变化。”浏览器的安全机制“浏览器安全机制核心是同源策略以及在此基础上扩展的 CORS、CSP、沙箱隔离 等。”“浏览器的安全基石是同源策略协议、域名、端口必须一致否则跨域请求被拦截。同源策略限制了 Cookie/LocalStorage 的跨域读取、DOM 访问和 AJAX 请求。安全的扩展机制包括CORS服务端配置允许跨域、CSP内容安全策略限制资源加载来源防XSS、沙箱隔离iframe的sandbox属性限制JS执行。HTTPS也是浏览器安全的重要一环防中间人攻击。”浏览器防止xss攻击“XSS跨站脚本攻击的核心是注入恶意脚本防御核心是 ‘不信任用户输入’对输入输出做过滤和转义。”“XSS分三种存储型恶意脚本存服务器、反射型恶意脚本在URL参数中、DOM型通过DOM操作注入。**防御措施**① 输出转义HTML实体编码 等特殊字符② CSP内容安全策略限制只能加载白名单内的脚本禁止内联 onclick 和 eval()③ Cookie 设置 HttpOnly禁止JS读取Cookie防止窃取Session④ 用户输入过滤对输入内容做白名单校验移除http和https的区别实现1rem100px假设设计稿是 750px 宽clientWidth / 7.5 就得到 100px这样 1rem 就等于 100px。再通过监听 resize 和 pageshow 事件在窗口变化或页面从缓存加载时重新计算保证适配实时更新。git基本操作git branch 新分支名称 新建分支git add . 把所有更改的文件添加到仓库git commit -mxxx 提交文件git pushflex-direction / flex-wrap / flex-flow / justify-content / align-items / align-contentopenid是表示用户在你的当前应用中的唯一标识多个应用同一个用户的openId可能并不相同。unionid同一用户对同一个微信开放平台下的不同应用是相同的
返回列表