卓世前端实习面试:盒模型、闭包与事件循环八股串烧
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 自我介绍。
- 介绍实习经历。
- 盒模型是什么?
- 如何水平垂直居中?有哪些方案?
- 数组有哪些常用的方法?
- 知道 axios 吗?用来干什么的?有哪些配置?
- 介绍一下 Promise。
- 介绍一下事件委托是什么?
- 介绍一下闭包是什么?
- 介绍一下 JS 事件循环。
- Vue 和 React 哪个更熟悉?讲解一下这两个框架的差异。
- 手写题:数组去重。
《参考解析》
盒模型的问题要答出「宽度到底算了什么」和它对布局的连带影响。 标准盒模型(content-box)里 width 只表示内容区,实际占位要加 padding 与 border;border-box 里 width 直接是内容加内边距加边框,所以做栅格和百分比布局时更好算。切换用 box-sizing,实践中通常全局设成 border-box。再往下能延伸的是外边距折叠(相邻兄弟与父子之间的 margin 会合并,取较大值,父元素加 padding 或触发 BFC 可以阻断)、行内元素的宽高不生效、以及 offsetWidth(含边框)与 getBoundingClientRect()(含 transform 后的视觉尺寸、可为小数)取值的差异。答到这一层,面试官会认为你不只是背过定义。
居中方案要一次列全,并说清各自的适用场景与副作用。 弹性布局最通用:父元素 display: flex 加 justify-content: center 与 align-items: center,多行或不定宽高都能用;网格布局一句 place-items: center 更短。绝对定位加 transform: translate(-50%, -50%) 适合脱离文档流的浮层,缺点是子元素尺寸变化不影响自身布局;margin: auto 配绝对定位可以在已知尺寸下居中块级元素。行内元素可以用 line-height 等于容器高度(只适合单行文本)。表格布局 display: table-cell 加 vertical-align: middle 是兼容旧浏览器的老办法。面试官真正想听的是「你知道为什么要用 flex 而不是继续用绝对定位」——能说清脱离文档流带来的高度塌陷与响应式问题,比罗列三种写法更好。
数组方法按「是否改变原数组」和「返回值是什么」两条线梳理,答起来最清楚。 会改原数组的:push / pop / shift / unshift / splice / sort / reverse / fill / copyWithin。不改的:slice / concat / join / indexOf / includes / flat / at。遍历与转换类:forEach(无返回值、不能中断)、map / filter / reduce / some / every / find / findIndex / flatMap。注意 sort 默认按字符串比较、数字排序要传比较函数;forEach 内部 return 不会跳出循环。追问常常接着手写题走:数组去重的几种写法各有边界——[...new Set(arr)] 最简洁但把 NaN 视为相同、-0 归一为 0,且只适合基本类型;对象数组去重要么用 Map 按某个 key 保留首个,要么用 JSON.stringify 做粗粒度判重(键序敏感、无法处理函数与 undefined);需要保持原始顺序且兼容老环境就用 filter 加 indexOf。能主动说出这些边界,手写题的分数会明显不同。
Promise 的答法从状态机出发,再落到微任务与组合方法。 它有 pending / fulfilled / rejected 三个状态、只能单向流转一次;then 返回新的 Promise,所以链式调用中回调的返回值会被自动包装(返回 Promise 会等它 resolve,抛错则把新 Promise 置为 rejected),这就是错误可以一路冒泡到最后一个 catch 的原因。回调被放进微任务队列,因此 then 里的代码总在同步代码之后、下一个宏任务之前执行。组合方法要能分清:all 等全部成功、一个失败就整体失败;allSettled 等全部落定并返回状态与结果;race 取第一个落定;any 取第一个成功。async/await 本质是 Promise 加生成器的语法糖,await 之后仍受微任务调度影响,在循环里 await 会串行化——这块常被追问「怎么改成并发」(先 map 出 Promise 数组再 Promise.all)。
闭包和事件委托都属于「能背定义但要讲出用途和风险」的题。 闭包是函数与其定义时词法作用域的组合,函数被带出原作用域后依然能访问那里的变量,用途包括私有变量、柯里化、防抖节流里保存定时器状态、以及循环里用 let 为每轮创建独立绑定(var 加立即执行函数是旧写法)。风险在于被闭包引用的变量不会被回收,长生命周期对象(全局事件监听、定时器、缓存)持有大对象就会造成内存增长,所以要在卸载时移除监听、清掉定时器。事件委托是把监听器挂在父元素上、利用事件冒泡统一处理子元素事件,好处是动态新增的子节点无需重新绑定、监听器数量从 N 个降到 1 个,常见于列表与表格;要注意 event.target 与 currentTarget 的区别、需要用 closest 找到真正关心的元素、以及 focus / blur 等不冒泡的事件要用捕获阶段的 focusin / focusout 替代。
事件循环要讲清「宏任务—微任务—渲染」的顺序和几个反直觉的点。 一次循环取出一个宏任务执行,执行期间产生的微任务会在当前宏任务结束后全部清空(微任务里再产生的微任务也会在同一轮清完),之后浏览器才可能渲染,再进入下一个宏任务。所以微任务里写长循环会饿死渲染、让页面卡死,而 setTimeout 的延迟只是最早可执行时间、并不保证准时;requestAnimationFrame 在渲染前执行,适合做动画。Node 与浏览器的事件循环阶段划分不同(process.nextTick 优先于 Promise 微任务),被问到时要主动区分环境。
Vue 与 React 的差异,最好落到「数据变化之后框架做了什么」。 Vue 用模板加响应式系统:通过 Proxy 追踪依赖,数据变化时精确知道哪些组件用了它、直接触发对应组件的更新,配合编译期的静态提升与补丁算法减少虚拟 DOM 比对量;React 走不可变数据加状态驱动渲染:setState 后从组件树顶往下重新执行函数组件,再靠 Fiber 调度把渲染工作切片、按优先级让出主线程,用 useMemo / memo 手动控制重渲染。心智差异是「Vue 帮你追踪、React 要求你返回新对象」,工程差异是模板与 JSX、指令与 Hook、以及生态与团队习惯。回答时给出自己熟悉哪一个、并说明「另一个为什么不影响我上手」,比强行比较优劣更合适。