2025年前端面试秘籍细选问题及答案_第1页
2025年前端面试秘籍细选问题及答案_第2页
2025年前端面试秘籍细选问题及答案_第3页
2025年前端面试秘籍细选问题及答案_第4页
2025年前端面试秘籍细选问题及答案_第5页
已阅读5页,还剩30页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

2025年前端面试秘籍细选问题及答案ES6中let/const与var的本质区别是什么?如何解决块级作用域中的循环闭包问题?let和const的核心差异在于变量的可变性:const声明的变量必须初始化且不可重新赋值(对象属性可修改),而let声明的变量可重新赋值。两者共同特性是块级作用域({}内有效)、不存在变量提升(TDZ暂时性死区)、同一作用域不能重复声明。var的缺陷在于函数作用域、变量提升(可能覆盖)、循环中闭包问题(所有循环内的函数共享同一个变量)。解决循环闭包的经典方案是使用立即执行函数(IIFE)包裹循环体,将当前迭代值绑定到独立作用域;ES6更推荐使用let声明循环变量,利用块级作用域为每次迭代创建独立绑定,例如:for(leti=0;i<5;i++){setTimeout(()=>console.log(i),0)}会输出0-4,而var声明则全部输出5。如何实现一个符合Promise/A+规范的简易Promise?需要处理哪些边界情况?核心步骤包括:状态管理(pending→fulfilled/rejected单向转换)、then方法的异步执行、值穿透(非函数参数忽略)、错误捕获。基础结构如下:classMyPromise{constructor(executor){this.state='pending';this.value=undefined;this.reason=undefined;this.onFulfilledCallbacks=[];this.onRejectedCallbacks=[];constresolve=(value)=>{if(this.state==='pending'){this.state='fulfilled';this.value=value;this.onFulfilledCallbacks.forEach(fn=>fn());}};constreject=(reason)=>{if(this.state==='pending'){this.state='rejected';this.reason=reason;this.onRejectedCallbacks.forEach(fn=>fn());}};try{executor(resolve,reject);}catch(e){reject(e);}}then(onFulfilled,onRejected){onFulfilled=typeofonFulfilled==='function'?onFulfilled:v=>v;onRejected=typeofonRejected==='function'?onRejected:e=>{throwe};constpromise2=newMyPromise((resolve,reject)=>{if(this.state==='fulfilled'){setTimeout(()=>{try{constx=onFulfilled(this.value);resolvePromise(promise2,x,resolve,reject);}catch(e){reject(e);}},0);}if(this.state==='rejected'){setTimeout(()=>{try{constx=onRejected(this.reason);resolvePromise(promise2,x,resolve,reject);}catch(e){reject(e);}},0);}if(this.state==='pending'){this.onFulfilledCallbacks.push(()=>{setTimeout(()=>{try{constx=onFulfilled(this.value);resolvePromise(promise2,x,resolve,reject);}catch(e){reject(e);}},0);});this.onRejectedCallbacks.push(()=>{setTimeout(()=>{try{constx=onRejected(this.reason);resolvePromise(promise2,x,resolve,reject);}catch(e){reject(e);}},0);});}});returnpromise2;}catch(onRejected){returnthis.then(null,onRejected);}}//关键辅助函数:处理then返回值x的解析逻辑functionresolvePromise(promise2,x,resolve,reject){if(x===promise2){//防止循环引用returnreject(newTypeError('Chainingcycledetected'));}if(xinstanceofMyPromise){//x是Promise实例x.then(resolve,reject);}elseif(typeofx==='object'&&x!==null||typeofx==='function'){//x是thenable对象letthen;try{then=x.then;}catch(e){returnreject(e);}if(typeofthen==='function'){letcalled=false;//防止多次调用resolve/rejecttry{then.call(x,y=>{if(called)return;called=true;resolvePromise(promise2,y,resolve,reject);},r=>{if(called)return;called=true;reject(r);});}catch(e){if(!called)reject(e);}}else{resolve(x);}}else{//普通值直接resolveresolve(x);}}边界情况包括:then回调非函数时的值穿透、Promise解析过程中thenable对象的递归处理、同一Promise多次调用then的回调收集、循环引用(如returnthis)的检测、同步executor中的错误捕获。React的Fiber架构解决了什么问题?其核心工作原理是什么?Fiber架构的核心目标是解决React15及之前版本的"栈调和"(StackReconciler)在复杂应用中导致的页面卡顿问题。栈调和使用递归进行虚拟DOMdiff,这是一个同步不可中断的过程,当计算量过大(超过16ms)时会阻塞主线程,导致动画/输入响应延迟。Fiber的核心设计是将调和过程(Reconciliation)拆分为多个可中断的任务单元(Fiber节点),每个单元执行完后检查是否有更高优先级的任务(如用户输入),若有则暂停当前任务,优先处理高优先级任务,待主线程空闲时再恢复。这种"可中断的异步调和"基于浏览器的requestIdleCallbackAPI(实际React使用自定义的调度器,因requestIdleCallback兼容性和精度问题)。Fiber的工作原理可分为两个阶段:1.协调阶段(ReconciliationPhase):此阶段是可中断的。React将组件树转换为Fiber树(每个Fiber节点对应一个组件/DOM元素/文本节点),每个Fiber节点保存了组件的状态、props、子节点等信息。通过"双缓存"技术(current树和workInProgress树),每次更新时基于current树创建workInProgress树,通过深度优先遍历为每个Fiber节点执行更新逻辑(计算state、props变化,提供新的子Fiber节点),标记需要更新的操作(插入/删除/更新)到副作用链表(effectlist)。2.提交阶段(CommitPhase):此阶段是同步不可中断的。将协调阶段确定的副作用(DOM更新、生命周期函数调用等)一次性应用到真实DOM上,并处理ref更新、调用useEffect回调等。Fiber节点的关键属性包括:type(组件类型)、key(唯一标识)、stateNode(对应的真实DOM或类组件实例)、return(父Fiber)、child(第一个子Fiber)、sibling(兄弟Fiber)、alternate(双缓存中的另一个副本)、effectTag(标记操作类型,如Placement/Update/Deletion)。Vue3的响应式系统相比Vue2有哪些改进?如何实现对数组和对象的深层响应?Vue3响应式系统的核心改进:1.基于ES6Proxy替代Vue2的Object.defineProperty:解决了对象新增/删除属性无法响应、数组索引和length属性修改无法检测的问题(Vue2通过重写数组方法实现部分响应)。Proxy可以拦截所有对象操作(13种捕获器),包括in、for...of等操作,覆盖更全面。2.更细粒度的依赖跟踪:Vue2的Dep和Watcher是1对多关系(每个属性对应一个Dep,收集所有依赖它的Watcher),而Vue3使用WeakMap结构(target→Map(key→Set(effect)))存储依赖,每个属性的依赖是独立的Set,避免了属性无关的Watcher被错误触发。3.组合式API(Composables)的支持:响应式系统与组件解耦,通过ref、reactive等工具函数可以在任意作用域创建响应式数据,更灵活地实现逻辑复用。深层响应的实现:-对象:当通过reactive创建响应式对象时,Proxy的get陷阱会递归检查子属性是否为对象,若为对象则用reactive包装(懒代理)。例如访问obj.a.b时,第一次访问obj.a会返回其代理对象,再次访问其b属性时同样触发代理。-数组:Proxy拦截数组的所有变更操作(如push、splice),在触发set陷阱时,除了更新对应索引的值,还会检查length属性的变化。由于数组的方法(如push)会改变数组长度和新增元素,Vue3在拦截这些方法时,会先执行原生方法,再触发依赖更新。-深层响应的限制:默认情况下,reactive是深层响应的,但如果修改对象的属性为非响应式值(如直接赋值为普通对象),需要手动用reactive包装。例如:constobj=reactive({a:{b:1}});obj.a={b:2};//此时新的{a:{b:2}}是普通对象,非响应式//应改为:obj.a=reactive({b:2});Vue3通过track函数收集依赖(在effect运行时,访问响应式属性时触发track,将当前effect加入该属性的依赖集合),通过trigger函数触发依赖(在修改响应式属性时触发trigger,遍历依赖集合执行effect)。对于深层对象,track和trigger会递归处理子属性,确保嵌套结构的变化被捕获。如何从0到1实现一个前端监控系统?需要考虑哪些核心指标和异常类型?前端监控系统的核心模块包括数据采集、数据传输、数据存储与分析、可视化展示。1.数据采集层:-异常监控:-JS错误:通过window.onerror和window.addEventListener('error')捕获同步错误;通过window.addEventListener('unhandledrejection')捕获Promise未处理rejection。-资源加载错误:监听error事件(需区分元素类型,如img、script的error事件)。-白屏/崩溃:通过MutationObserver监控DOM变化,若长时间无节点变更则判定为白屏;利用PerformanceAPI计算FCP(FirstContentfulPaint),若超过阈值(如5s)则记录。-性能监控:-核心指标:FCP(首次内容渲染)、LCP(最大内容渲染)、TTI(可交互时间)、TBT(总阻塞时间)、CLS(累积布局偏移),可通过PerformanceAPI和Lighthouse的metrics获取。-自定义指标:接口耗时(通过XMLHttpRequest/fetch的hook)、路由切换时间(记录beforeRouteEnter和afterRouteEnter的时间差)、关键组件渲染时间(使用Performance.mark和measure)。-用户行为:-点击流:记录用户点击的元素路径(通过event.target.closest获取DOM路径)。-路由轨迹:监听前端路由变化(hashchange或popstate),记录访问路径和停留时间。-页面滚动:使用IntersectionObserver监听关键区域的曝光,或记录scroll事件的位置和时间。2.数据传输层:-压缩与采样:对大段数据(如堆栈信息)进行压缩(如LZW算法),对高频事件(如scroll)进行采样(每100ms记录一次)。-传输方式:优先使用navigator.sendBeacon(支持离线发送,不阻塞页面卸载),其次使用XMLHttpRequest或fetch。-失败重试:对传输失败的数据,存入localStorage,下次页面加载时重新发送。3.数据存储与分析:-存储方案:使用Elasticsearch(支持全文检索)或ClickHouse(列式存储,适合大数据量分析)。-聚合处理:对错误进行去重(基于错误信息、堆栈、发生页面),计算错误率(错误次数/页面访问次数),按版本、浏览器、操作系统分类统计。4.可视化展示:-实时看板:展示错误趋势、性能指标分位数(p75/p95)、TOP10错误。-详情钻取:支持查看具体错误的堆栈信息、用户设备信息、发生时的上下文(如路由、用户操作路径)。-报警系统:设置阈值(如错误率超过5%),通过邮件/钉钉/企业微信推送报警。核心考虑点:-性能影响:采集逻辑需最小化对主线程的占用(如使用微任务/requestIdleCallback执行数据处理)。-数据准确性:避免重复上报(如同一错误在同一个页面多次触发时去重),确保用户隐私(脱敏处理cookie、用户ID等敏感信息)。-可扩展性:支持自定义插件(如第三方库错误监控、特定业务场景的埋点)。如何设计一个支持动态主题切换的前端系统?需要考虑哪些技术方案和兼容性问题?动态主题切换的核心是实现样式变量的动态更新,常见方案包括:1.CSS变量(CSSCustomProperties)方案:-原理:利用CSS的--var语法定义变量,通过JavaScript动态修改:root选择器下的变量值。-实现步骤:a.在全局样式中定义基础变量::root{--primary-color:409eff;--bg-color:fff;}b.业务样式中使用变量:.button{background:var(--primary-color);}c.切换主题时,通过document.documentElement.style.setProperty('--primary-color','ff4d4f')修改变量值。-优势:浏览器原生支持,无需额外构建步骤,切换即时生效(无样式闪烁)。-限制:IE完全不支持(需配合PostCSS插件降级),部分旧版浏览器(如Safari10)对变量的继承和作用域支持不完善。2.预处理器变量(Sass/LESS)动态加载方案:-原理:为每个主题预编译对应的CSS文件(如theme-default.css、theme-dark.css),切换时动态加载目标CSS文件。-实现步骤:a.在构建时提供多个主题文件,通过变量覆盖实现差异://theme-dark.less@import"base.less";@primary-color:333;@bg-color:000;b.切换主题时,创建新的link标签,加载目标主题文件,并移除旧的主题文件。-优势:兼容所有浏览器,支持复杂样式(如混合器、函数)。-限制:切换时可能出现样式闪烁(需预加载主题文件),多主题文件增大包体积(可通过CDN缓存优化)。3.CSS-in-JS方案(如styled-components、emotion):-原理:通过主题提供者(ThemeProvider)组件传递主题对象,样式函数中访问主题变量。-实现示例(styled-components):constThemeProvider=styled.ThemeProvider;constButton=styled.button`background:${props=>props.theme.primaryColor};`;//切换主题<ThemeProvidertheme={darkTheme}><Button>DarkButton</Button></ThemeProvider>-优势:与组件深度集成,支持动态计算主题值(如根据父组件状态调整)。-限制:需额外学习成本,运行时计算可能影响性能(可通过memo优化)。兼容性处理:-对不支持CSS变量的浏览器(如IE),可降级为预编译方案,通过特性检测(如!window.CSS||!CSS.supports('color','var(--test)'))加载对应的主题文件。-动态加载CSS文件时,使用rel="preload"预加载其他主题文件,避免切换时的网络延迟。-避免在关键路径样式中使用动态变量(如首屏背景色),可通过内联基础样式或服务端渲染(SSR)输出初始主题样式。如何用JavaScript实现一个高效的虚拟列表?需要考虑哪些边界情况?虚拟列表用于渲染长列表(如10000条数据),仅渲染可见区域的内容,通过滚动时动态更新渲染项,减少DOM节点数量,提升性能。核心实现步骤:1.计算列表总高度:总高度=项数量×项高度(固定高度)或动态计算(可变高度需缓存每项高度)。2.确定可见区域的起始和结束索引:-滚动偏移量(scrollTop):当前滚动条的垂直位置。-视口高度(viewportHeight):容器可见区域的高度。-起始索引(startIdx):Math.floor(scrollTop/itemHeight)。-结束索引(endIdx):Math.ceil((scrollTop+viewportHeight)/itemHeight)。3.渲染可见项:截取数据数组中[startIdx,endIdx]的子数组,渲染对应的DOM节点。4.处理偏移量:通过transform:translateY(${startIdx×itemHeight}px)将渲染项定位到正确位置,保持列表整体滚动效果。动态高度的优化(使用react-virtualized的思路):-缓存每项高度:使用数组存储每项的高度(初始可设为默认值,渲染后通过getBoundingClientRect获取真实高度更新缓存)。-计算滚动偏移量对应的起始索引:使用二分查找在缓存的高度累加数组中查找scrollTop的位置。-计算总高度:累加所有项的高度。关键代码示例(固定高度):classVirtualListextendsReact.Component{state={startIdx:0,endIdx:0};listRef=React.createRef();wrapperRef=React.createRef();componentDidMount(){this.calculateVisibleItems();this.wrapperRef.current.addEventListener('scroll',this.handleScroll);}componentWillUnmount(){this.wrapperRef.current.removeEventListener('scroll',this.handleScroll);}calculateVisibleItems=()=>{constwrapper=this.wrapperRef.current;constlist=this.listRef.current;constitemHeight=ps.itemHeight;constscrollTop=wrapper.scrollTop;constviewportHeight=wrapper.clientHeight;conststartIdx=Math.max(0,Math.floor(scrollTop/itemHeight));constendIdx=Math.min(ps.data.length,Math.ceil((scrollTop+viewportHeight)/itemHeight)+1//预加载1项防止滚动空白);this.setState({startIdx,endIdx});};handleScroll=()=>{this.calculateVisibleItems();};render(){const{data,itemHeight,renderItem}=ps;const{startIdx,endIdx}=this.state;constvisibleData=data.slice(startIdx,endIdx);constoffset=startIdxitemHeight;return(<divclassName="wrapper"ref={this.wrapperRef}style={{height:'600px',overflow:'auto'}}><divclassName="list"ref={this.listRef}style={{height:`${data.lengthitemHeight}px`}}><divstyle={{transform:`translateY(${offset}px)`}}>{visibleData.map((item,index)=>(<divkey={item.id}style={{height:`${itemHeight}px`}}>{renderItem(item)}</div>))}</div></div></div>);}}边界情况处理:-快速滚动时的性能:使用requestAnimationFrame优化scroll事件处理,避免频繁重新渲染。-项高度动态变化:缓存高度并更新总高度和偏移量,可能需要使用ResizeObserver监听项的尺寸变化。-列表为空或数据量少:当数据量小于视口可展示数量时,正常渲染所有项。-虚拟列表与其他滚动容器嵌套:需正确计算外层容器的滚动偏移量(如使用getBoundingClientRect获取相对于视口的位置)。-键盘导航或锚点跳转:监听scroll事件的同时,处理通过JS设置scrollTop的情况(如跳转到指定项)。如何设计一个支持无限级分类的树形组件?需要考虑哪些交互和性能优化?无限级树组件的核心是递归渲染和高效的状态管理,需支持展开/折叠、选中、搜索等高阶交互。1.数据结构设计:每个节点需包含唯一标识(id)、显示名称(label)、子节点数组(children)、展开状态(expanded)、选中状态(checked)等属性。示例:{id:'1',label:'一级节点',children:[{id:'1-1',label:'二级节点',children:[...]}],expanded:false,checked:false}2.递归渲染:使用递归组件或高阶函数,根据节点的children是否存在决定是否渲染子节点。例如React中的实现:functionTreeNode({node,onToggle,onCheck}){return(<divclassName="tree-node"><divclassName="node-header"><spanclassName="toggle-btn"onClick={()=>onToggle(node.id)}>{node.expanded?'−':'+'}</span><inputtype="checkbox"checked={node.checked}onChange={()=>onCheck(node.id)}/><span>{node.label}</span></div>{node.expanded&&node.children&&node.children.length>0&&(<divclassName="children">{node.children.map(child=>(<TreeNodekey={child.id}node={child}onToggle={onToggle}onCheck={onCheck}/>))}</div>)}</div>);}3.交互处理:-展开/折叠:点击节点时切换expanded状态,递归更新父节点的展开状态(可选)。-复选框联动:使用半选状态(indeterminate),当子节点部分选中时父节点显示半选;点击父节点时全选/全不选子节点(需递归更新子节点状态)。-搜索过滤:根据关键词递归过滤节点,保留匹配的节点及其所有祖先节点(确保路径可见)。4.性能优化:-虚拟滚动:对深层级树使用虚拟列表技术,仅渲染可见区域的节点(需计算节点的垂直位置)。-记忆化渲染:使用React.memo或useMemo缓存TreeNode组件,仅在节点的label、expanded、checked变化时重新渲染。-事件委托:将点击事件绑定在树容器上,通过事件目标的dataset获取节点id,避免为每个节点绑定事件。-异步加载子节点:对于大数据量的树,使用懒加载(点击展开时通过API获取子节点数据),减少初始加载时间。5.边界情况:-循环引用:确保数据中不存在循环的children引用(如A的子节点是B,B的子节点是A),需在数据校验阶段拦截。-大量子节点:当单个节点的子节点数量极大时(如1000+),使用虚拟列表渲染子节点列表。-键盘导航:支持通过上下箭头键移动焦点,Enter键展开/折叠,Space键切换选中状态。-拖放排序:实现节点的拖放时,需更新父节点的children数组,并处理跨层级拖放(如将子节点拖到另一个父节点下)。在Webpack5中,模块联邦(ModuleFederation)的核心应用场景是什么?如何配置一个基础的跨应用共享组件?模块联邦的核心价值是实现跨前端应用的模块共享,支持在运行时动态加载其他应用暴露的模块,打破传统微前端中通过iframe或全局变量通信的限制,实现更细粒度的代码共享(如共享React组件、工具函数)。典型应用场景:-微前端架构:主应用加载多个子应用的入口组件,子应用之间共享通用组件(如导航栏、表单组件)。-多团队协作:不同团队维护的应用共

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论