面试了个中高级前端,连闭包输出题都答不上

0 阅读14分钟

今天面试了一个中高级前端开发候选人,说出来有点颠覆认知——她居然连闭包+事件循环的输出顺序题都答不上,这可是前端面试的入门必修题。

她的简历堪称完美:计算机专业、3年大厂经验、熟练使用Vue和React,还写着“主导过复杂后台管理系统重构”。可当我抛出第一个基础代码题,她的反应就让我瞬间清醒。

👉 Q1:这段代码的输出结果是什么?为什么?

for (var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i)
  }, 1000)
}
console.log('end')

她:“输出 0 1 2 3 4 吧?setTimeout 延迟执行,循环完了输出。”

✅ 前端必懂:输出结果是 end 然后 5 5 5 5 5。var 没有块级作用域,循环结束时 i 已经变成 5,setTimeout 回调执行时访问的是同一个 i。考察点:作用域、事件循环、闭包。如果要输出 0-4,需要用 let 或者闭包(IIFE)保存每次循环的值。

👉 Q2:用 let 改成上面的代码,输出是什么?如果 setTimeout 延迟改成 0 呢?

她:“let 有块级作用域,应该输出 0 1 2 3 4。延迟 0 也是 0 1 2 3 4 吧?”

✅ 理想回答:用 let 确实输出 0 1 2 3 4。但如果延迟改成 0,输出顺序是 end 然后 0 1 2 3 4。因为 setTimeout 即使是 0 也会被放入任务队列,等主线程同步代码执行完才执行。考察点:宏任务/微任务、事件循环优先级。

👉 Q3:不用 let,怎么用闭包实现输出 0 1 2 3 4?

她:(沉默)...用闭包?不知道怎么改。

✅ 前端必会:

for (var i = 0; i < 5; i++) {
  (function(j) {
    setTimeout(() => {
      console.log(j)
    }, 1000)
  })(i)
}

考察点:闭包的定义——函数能够记住并访问它的词法作用域,即使这个函数在作用域之外执行。IIFE 每次循环创建一个新的作用域,把 i 的值保存在参数 j 中。

👉 Q4:如果我想实现每隔 1 秒输出一个数字,0 1 2 3 4,应该怎么改造?

她:...可以用 async/await 加 sleep 吧?具体怎么写不太确定。

✅ 进阶方案:

// 方案一:递归 + setTimeout
let i = 0
function print() {
  if (i < 5) {
    console.log(i++)
    setTimeout(print, 1000)
  }
}
print()

// 方案二:Promise + reduce 串行
[0,1,2,3,4].reduce((promise, num) => {
  return promise.then(() => {
    return new Promise(resolve => {
      setTimeout(() => {
        console.log(num)
        resolve()
      }, 1000)
    })
  })
}, Promise.resolve())

考察点:异步编程能力、Promise 串行控制、递归思想。

面试到这里,我已经知道结果了。不是我苛刻,而是中高级前端岗,“JavaScript 基础是底线”。

很多人觉得,做前端只要会用框架就行,没必要深究闭包、原型链、事件循环。但事实是,不懂这些底层原理,遇到复杂交互的性能问题根本找不到优化方向;更别说做组件库设计、写 BFF 层、做工程化基建,只能停留在“调 API 画页面”的初级阶段。

前端赛道很卷,想拿高薪的人很多,但技术岗拼的从来不是简历包装,而是硬实力。

提醒所有想冲击中高级前端的朋友:先吃透 JavaScript 核心概念,搞懂执行上下文、闭包、原型链、事件循环这些底层逻辑,再去学框架源码、工程化工具,才能真正站稳脚跟。别让“连闭包输出题都答不上”,断送自己的面试和职业路。


以下面试题: bf9336313a2d642c886c1134c09f2827.png

飞书 PDF.png

JavaScript

1. 不会冒泡的事件有哪些

在 JavaScript 和浏览器中,绝⼤多数事件都会按照 DOM 事件流模型冒泡,即事件会从⽬标元素开始向上冒泡到它的⽗元素,并最终到达 document 元素。然⽽,也有⼀些事件是不会冒泡的。这些事件通常直接在⽬标元素上触发,并不会向上传播。

以下是⼀些不会冒泡的事件的⽰例:

  1. focus :当元素获得焦点时触发,例如通过键盘或⿏标点击。这是⼀个不会冒泡的事件。
  2. blur :当元素失去焦点时触发。这也是⼀个不会冒泡的事件。
  3. focusin :与 focus 类似,但会在元素或其⽗元素上触发(冒泡),因此这个事件是特例。
  4. focusout :与 blur 类似,但会在元素或其⽗元素上触发(冒泡),因此这个事件是特例。
  5. load :当图像、⾳频、视频或其他资源加载完成时触发。例如,在 img 元素上触发的 load 事件不会冒泡。
  6. unload :当⻚⾯即将被导航离开时触发。这通常⽤于执⾏清理⼯作,也不会冒泡。
  7. stop :通常与 media 元素相关,例如 audio 或 video 元素。这是在媒体播放停⽌时触发的事件。
  8. readystatechange :当 document 的 readyState 改变时触发。这通常在⻚⾯加载时使⽤。
  9. scroll :当元素滚动时触发。这个事件在某些浏览器中可能会冒泡。

这些事件通常直接在⽬标元素上触发,并且不会传播到⽗元素上。

1 javascript.png

  • 2.mouseEnter 和 mouseOver 有什么区别?
  • 3.MessageChannel 是什么,有什么使用场景?
  • 4. async、await 实现原理
  • 5.Proxy 能够监听到对象中的对象的引用吗?
  • 6.如何让 var [a, b]= {a: 1,b:2}解构赋值成功?
  • 7.下面代码会输出什么?
  • 8.描述下列代码的执行结果
  • 9.什么是作用域链?
  • 10.bind、call、apply 有什么区别?如何实现-个bind?
  • 11.common.js和es6中模块引入的区别?
  • 12.说说 vue3 中的响应式设计原理
  • 13.saript标签放在header里和放在body底部里有什么区别?
  • 14.下面代码中,点击“+3”按钮后,age 的值是什么?
  • 15.Vue中,created和mounted两个钩子之间调用时间差值受什么影响?
  • 16.vue中,推荐在哪个生命周期发起请求?
  • ......

性能优化

1. script标签放在header⾥和放在body底部⾥有什么区别?

将 script 标签放在 head 和 body 底部,会对⻚⾯的加载和性能产⽣不同的影响:script 标签放在 head 部分

优点:

  1. 预加载: 浏览器在渲染⻚⾯之前,会先下载和解析所有在 head 部分的脚本⽂件。这样可以确保脚本在⻚⾯加载过程中随时可以被调⽤。
  2. 全局可⽤性: ⼀些脚本,特别是需要在⻚⾯⼀加载就运⾏的脚本,适合放在 head 中。

缺点:

  1. 阻塞渲染: 浏览器在遇到 script 标签时会暂停 HTML 的解析和渲染,直到脚本下载并执⾏完毕。这可能会导致⻚⾯加载变慢,尤其是当脚本⽂件较⼤或者需要从远程服务器下载时。
  2. ⻚⾯⽩屏时间延⻓: ⽤⼾可能会看到⻚⾯在加载过程中有⼀段时间的⽩屏,直到脚本加载完成。script 标签放在 body 底部

优点:

  1. ⾮阻塞渲染: 将 script 标签放在 body 底部意味着浏览器可以优先下载和渲染 HTML 内容,这样⽤⼾可以更快地看到⻚⾯内容。
  2. 更好的⽤⼾体验: ⽤⼾不会因为等待脚本加载⽽⻓时间看到空⽩⻚⾯。⻚⾯内容会先显⽰出来,然后再执⾏脚本,这提⾼了⻚⾯的响应速度和⽤⼾体验。

缺点:

  1. 延迟脚本执⾏: 如果某些脚本需要在⻚⾯加载之前运⾏(如某些初始化脚本),放在 body 底部可能会导致这些脚本运⾏延迟,影响功能。

现代优化技术

  1. defer 属性: 在 head 部分使⽤ script 标签时,可以添加 defer 属性。这个属性会告诉浏览器异步下载脚本,但在⻚⾯解析完成后再执⾏脚本。这样既可以保持脚本全局可⽤,⼜不会阻塞⻚⾯渲染。
1 <script src="script.js" defer></script>

2. async 属性: async 属性也⽤于异步加载脚本,但它会在脚本下载完成后⽴即执⾏,不考虑⻚⾯的解析进度。这对某些独⽴的、不会依赖于其他脚本或 DOM 结构的脚本很有⽤。

1 <script src="script.js" async></script>

总结

• 部分: 适合需要⽴即执⾏的脚本,但可能阻塞⻚⾯渲染。

• 底部: 适合⼀般脚本,能提⾼⻚⾯加载性能和⽤⼾体验。

• 使⽤ defer 或 async : 现代浏览器⽀持这些属性,可以同时兼顾性能和功能需求。

10 性能优化.png

  • 2.前端性能优化指标有哪些?怎么进行性能检测?
  • 3.SPA(单页应用)首屏加载速度慢怎么解决?
  • 4.如果使用CSS提高贞面性能?
  • 5.怎么进行站点内的图片性能优化?
  • 6.虚拟DOM一定更快吗?
  • 7.有些框架不用虚拟dom,但是他们的性能也不错是为什么?
  • 8.如果某个页面有几百个函数需要执行,可以怎么优化页面的性能?
  • 9.讲一下png8、png16、png32的区别,并简单讲讲 png 的压缩原理
  • 11.React.memo()和 useMemo()的用法是什么,有哪些区别?
  • 12.导致页面加载白屏时间长的原因有哪些,怎么进行优化?
  • 13.如果一个列表有 100000 个数据,这个该怎么进行展示?
  • 14.DNS 预解析是什么?怎么实现?
  • 15.在 React 中可以做哪些性能优化?
  • 16.浏览器为什么要请求并发数限制?
  • ......

React

1. 下⾯代码中,点击 “+3” 按钮后,age 的值是什么?

1 import { useState } from 'react';
2
3 export default function Counter() {
4 const [age, setAge] = useState(42);
5 function increment() {
6 setAge(age + 1);
7 }
8 return (
9 <>
10 <h1>Your age: {age}</h1>
11 <button onClick={() => {
12 increment();
13 increment();
14 increment();
15 }}>+3</button>
16 </>
17 );
18 }

点击 +3 时,可能只更新为 43。

这是因为 setAge(age + 1) 即使多次调⽤,也不会⽴即更新组件状态,⽽是会进⾏合并,最终只触发⼀次重新渲染。

如果要实现调⽤三次就增加 3 ,可以将 increment 改为函数式更新:

1 function increment() {
2 setAge(a => a + 1); // 函数式更新
3 }

4 react.png

  • 2.React Portals 有什么用?
  • 3.react 和 react-dom 是什么关系?
  • 4. React 中为什么不直接使用 requestIdleCallback?
  • 5.为什么 react 需要 fiber 架构,而 Vue 却不需要?
  • 6.子组件是一个 Portal,发生点击事件能冒泡到父组件吗?
  • 8.说说React render方法的原理?在什么时候会被触发?
  • 9.说说React事件和原生事件的执行顺序
  • 10.说说对受控组件和非受控组件的理解,以及应用场景?
  • 11.你在React项目中是如何使用Redux的?项目结构是如何划分的?
  • 12.说说对Redux中间件的理解?常用的中间件有哪些?实现原理?
  • 13.说说你对Redux的理解?其工作原理?
  • 14.说说你对immutable的理解?如何应用在react项目中?
  • 15.说说React ]sx转换成真实DOM过程?
  • 16.说说你在React项目是如何捕获错误的?
  • ......

Vue

1. Vue 有了数据响应式,为何还要 diff ?

Vue 中的数据响应式和虚拟 DOM 的 diff 算法是两个不同的概念,它们分别解决了不同的问题,相互协作以提⾼⻚⾯渲染的效率和性能。

数据响应式

Vue 的数据响应式系统通过 Object.defineProperty 或者 ES6 的 Proxy 来实现,主要解决了以下问题:

  1. 数据绑定:保证了视图与数据的同步更新,当数据发⽣变化时,视图会⾃动更新,避免了⼿动操作DOM 的繁琐和易出错性。
  2. 依赖追踪:Vue 能够追踪每个数据的依赖关系,即哪些组件或者计算属性依赖于某个数据。当数据变化时,⾃动更新依赖的组件或者计算属性。

虚拟 DOM 和 Diff 算法

虚拟 DOM 是⼀种内存中的表⽰结构,它是对真实 DOM 的抽象。Diff 算法是⼀种⾼效更新 DOM 的策略,它通过⽐较新旧虚拟 DOM 树的差异,最⼩化了更新操作,提⾼了⻚⾯的渲染效率。

为什么还需要 Diff 算法?

  1. 性能优化:直接操作真实 DOM 是⾮常昂贵的,⽽虚拟 DOM 可以在内存中快速进⾏⽐较和计算差异。Diff 算法帮助减少了更新操作的次数和范围,从⽽提升了⻚⾯渲染的性能。
  2. 批量更新:Diff 算法能够将多次 DOM 更新操作合并为⼀次,避免了频繁的 DOM 操作,减少了浏览器的重排和重绘。
  3. 跨平台兼容:虚拟 DOM 和 Diff 算法使得 Vue 可以运⾏在不同的平台上,例如浏览器、Weex 等,统⼀了渲染逻辑和数据响应式的实现。
  4. 更新效率:即使是响应式系统可以⾃动更新视图,但是如果每次数据变化都直接操作真实 DOM,可能会带来性能问题。Diff 算法可以智能地⽐较新旧 DOM 树的变化,只更新必要的部分,从⽽提⾼了更新效率。

综合作⽤

Vue 的数据响应式系统和虚拟 DOM + Diff 算法是紧密协作的:

• 数据响应式:保证了数据和视图的同步更新,提供了便捷的开发⽅式。

• 虚拟 DOM + Diff 算法:提⾼了⻚⾯渲染的效率和性能,减少了不必要的 DOM 操作,确保了⻚⾯的流畅性和响应性。

总体来说,数据响应式和 Diff 算法是为了解决不同层⾯的问题,结合起来使得 Vue 能够提供⾼效、流畅的⽤⼾体验。

16 工程化.png

  • 2.vue3 为什么不需要时间分片?
  • 3.vue3 为什么要引入 Composition API?
  • 4.谈谈 Vue 事件机制,并手写onon、off、emitemit、once
  • 5.computed 计算值为什么还可以依赖另外-个 computed 计算值?
  • 6.说-下 vm.$set 原理
  • 7. 怎么在 Wue 中定义全局方法?
  • 8.Vue 中父组件怎么监听到子组件的生命周期?
  • 10.说说 vue3 中的响应式设计原理
  • 11.Vue中,created和mounted两个钩子之间调用时间差值受什么影响?
  • 12.vue中,推荐在哪个生命周期发起请求?
  • 13.为什么 react 需要 fiber 架构,而 Vue 却不需要?
  • 14.SPA(单页应用)首屏加载速度慢怎么解决?
  • 15.说下Vite的原理
  • 16.Vue2.0为什么不能检查数组的变化,该怎么解决?
  • ......

工程化

1. package.json ⽂件中的 devDependencies 和 dependencies 对象有什么区别?

前端项⽬的 package.json ⽂件中, dependencies 和 devDependencies 对象都⽤于指定项⽬所依赖的软件包,但它们在项⽬的开发和⽣产环境中的使⽤有所不同。

  1. dependencies:

◦ dependencies 是指定项⽬在⽣产环境中运⾏所需要的依赖项。

◦ 这些依赖项通常包括运⾏时需要的库、框架、⼯具等。

◦ 当你通过 npm install 或 npm ci 安装依赖时,默认会安装 dependencies 中的包。

◦ 这些依赖项会被打包和部署到⽣产环境中,因此它们对于项⽬的运⾏是必需的。

  1. devDependencies:

◦ devDependencies 是指定在开发过程中所需要的依赖项。

◦ 这些依赖项通常包括开发、测试、构建、部署等过程中所需的⼯具、库等。

◦ 例如,测试框架、构建⼯具、代码检查⼯具等通常属于 devDependencies 。

◦ 当你在开发环境中使⽤ npm install 安装依赖时,只会安装 dependencies 中的包。要安装 devDependencies 中的包,你需要额外使⽤ npm install --dev 或 npm

install --only=dev 等命令。

◦ 这些依赖项不会被打包到⽣产环境中,因为它们只在开发过程中需要,对于实际部署和运⾏项⽬并不需要。

总的来说, dependencies 中的依赖项是项⽬运⾏所必需的,⽽ devDependencies 中的依赖项则是在开发过程中需要的辅助⼯具和库。

  • 2.webpack5 的主要升级点有哪些?
  • 3.说下vite的原理
  • 4.与webpack类似的工具还有哪些?区别?
  • 5.说说如何借助webpack来优化前端性能?
  • 6.说说webpack proxy工作原理?为什么能解决跨域?
  • 7.说说webpack的热更新是如何做到的?原理是什么?
  • 8.面试官:说说Loader和Plugin的区别?编写Loader,Plugin的思路?
  • 9.说说webpack中常见的Plugin?解决了什么问题?
  • 10.说说webpack中常见的Loader?解决了什么问题?
  • 11.说说webpack的构建流程?
  • 12.说说你对webpack的理解?解决了什么问题?
  • 13.webpack loader 和 plugin 实现原理
  • 14.如何提高webpack的构建速度?
  • 15.说说 webpack-dev-server 的原理
  • 16.你对 babel 了解吗,能不能说说几个 stage 代表什么意思?
  • ......

d7e197fc-bec6-46ac-ba1d-7497bb020101.png