前端面试血泪史(1)

221 阅读10分钟

背景介绍

最近刚开始面试,整个过程下来感触颇深。这是一家小型的互联网公司,问的问题也比较基础,这里小编是第一次面试,非常紧张,以至于答的不是很好,不过还是想和大家分享一下。

面试过程记录

自我介绍环节

这个环节我准备得比较充分,大概说了2分钟:

"面试官,您好,我是XXX,是XXX大学XXX学院的学生。我先介绍一下我的学习经历。

......(学习经历,怎样接触到前端的,怎样学习的,对前端的看法等等)"

面试官点点头,然后开始进入技术环节。

CSS基础 - 元素显示类型

面试官: "说说CSS中block、inline、inline-block的区别?"

我当时心想这个简单啊,但还是仔细组织了一下语言:

"这三种是CSS中最基本的显示类型:

block元素

  • 独占一行,宽度默认占满父容器
  • 可以设置width、height、margin、padding
  • 常见的有div、p、h1-h6等

inline元素

  • 在同一行显示,只占内容实际宽度
  • 不能设置width、height
  • 垂直方向的margin、padding不会生效
  • 典型的如span、a、em等

inline-block元素

  • 结合了前两者特点,既能在同一行显示,又能设置宽高
  • 可以设置所有的盒模型属性
  • 常用于制作导航菜单、按钮组等"

面试官继续问:"inline-block有什么缺点吗?"

"主要是会产生空白间隙问题,因为HTML中的换行符会被解析成空格。解决方法有几种:父元素设置font-size:0,或者子元素写在同一行,或者用浮动、flex布局替代。"

JavaScript基础 - 相等性判断

面试官: "== 和 === 有什么区别?能举几个例子吗?"

这个问题我还是做了准备的:

"== 是抽象相等,会进行类型转换;=== 是严格相等,不会转换类型。

"==" 会进行隐式类型转换,比较值之前会尝试将操作数转换为相同类型,如果类型相同,直接比较值。 如果类型不同,JavaScript会尝试转换类型后再比较。

"==="不进行类型转换,要求值和类型必须完全相同。如果类型不同,直接返回false。 如果类型相同,再比较值是否相等。

几个经典例子:

'2' == 2        // true,字符串转数字
'2' === 2       // false,类型不同

null == undefined    // true,特殊规则
null === undefined   // false,类型不同

0 == false      // true,都转为数字0
0 === false     // false,类型不同

[] == ![]       // true,这个比较坑

最后那个例子,![]先转为false,然后[]转为0,false转为0,所以相等。这就是为什么我们项目中强制使用===的原因,避免意外的类型转换。"

工程化 - Webpack相关

面试官: "简单说说webpack的打包原理?"

我稍微思考了一下,这个问题比较宏观:

"Webpack的核心是模块化打包:

  1. 入口分析:从entry开始,解析代码中的import/require语句
  2. 依赖图构建:递归分析所有依赖,形成依赖关系图
  3. 模块转换:通过各种loader处理不同类型文件,如babel处理JS,css-loader处理CSS
  4. 代码生成:将所有模块打包成一个或多个bundle
  5. 插件处理:在整个流程中,plugin可以在特定阶段执行额外操作

最终生成的bundle本质上是一个IIFE,内部维护了模块映射表和require函数来实现模块的加载。"

面试官: "在实际项目中遇到过webpack兼容性问题吗?怎么解决的?"

这个问题让我想起了之前的痛苦经历:

"遇到过几次比较头疼的兼容性问题:

IE11兼容性

  • 很多ES6+语法不支持,需要通过babel-preset-env配置targets
  • Promise、Array.includes等需要引入polyfill
  • CSS变量不支持,要用PostCSS插件转换

Node版本问题

  • webpack5要求Node 10.13+,但项目环境是Node 8
  • 最终选择降级到webpack4,配合相应的loader版本

模块解析问题

  • 某个第三方库用的CommonJS,但我们项目是ESM
  • 通过webpack的resolve.alias和externals配置解决

解决思路主要是:确定target环境 → 配置babel转换 → 添加polyfill → 调整webpack配置。现在我都会在项目初期就确定兼容性要求,避免后期改动成本过高。"

数据结构与算法

面试官: "说说链表这种数据结构,它有什么特点?"

"链表是一种线性数据结构,主要特点:

优势

  • 动态大小,不需要预分配内存
  • 插入删除操作效率高,O(1)时间复杂度
  • 内存利用率高,按需分配

劣势

  • 随机访问慢,需要O(n)时间遍历
  • 需要额外空间存储指针
  • 缓存局部性差,容易产生cache miss

在前端开发中,虽然直接用链表的场景不多,但很多地方用到了链表思想,比如React的Fiber架构就是链表结构,方便中断和恢复渲染过程。"

面试官: "讲一下冒泡排序?"

"冒泡排序是最基础的排序算法之一,它的核心思想就是通过相邻元素的比较和交换来实现排序。

具体来说,冒泡排序是这样工作的:每次遍历数组时,都会比较相邻的两个元素,如果前面的元素比后面的大,就交换它们的位置。这样一轮下来,最大的元素就会"冒泡"到数组的末尾,就像水中的气泡一样往上冒。

我举个简单的例子,比如数组[5, 2, 8, 1]:

  • 第一轮:5和2比较,5>2,交换得到[2, 5, 8, 1];然后5和8比较,不交换;8和1比较,8>1,交换得到[2, 5, 1, 8]。第一轮结束,最大值8到了末尾。
  • 第二轮:只需要比较前三个元素,最终5会冒泡到倒数第二个位置。
  • 依此类推,直到整个数组有序。

时间复杂度方面,最坏情况下需要比较n²次,所以是O(n²);最好情况下如果数组已经有序,只需要一轮遍历,是O(n)。空间复杂度是O(1),因为只需要一个临时变量来交换元素。

虽然冒泡排序效率不高,但它的优点是实现简单,易于理解,而且是稳定排序,相同元素的相对位置不会改变。在实际项目中,我们一般不会用冒泡排序处理大数据量,但它很适合用来理解排序算法的基本思想。"

面试官: "讲讲哈希?"

首先说什么是哈希。 哈希就是通过哈希函数将任意长度的输入数据映射成固定长度的输出值,这个输出值我们叫做哈希值或散列值。比如我们常见的MD5、SHA256都是哈希算法。 哈希有几个重要特性:

  • 第一是确定性,同样的输入永远产生同样的输出;
  • 第二是单向性,很难从哈希值反推出原始数据;
  • 第三是雪崩效应,输入的微小变化会导致输出完全不同。

在实际应用中,哈希主要有这几个用途:

  • 一是数据结构,比如哈希表,通过哈希函数快速定位数据位置,实现O(1)的查找效率;
  • 二是密码存储,我们不直接存储用户密码,而是存储密码的哈希值;
  • 三是数据完整性校验,比如文件下载后通过MD5值验证文件是否完整;
  • 四是数字签名和区块链等场景。

但是哈希也有一些缺点:

  • 最主要的是哈希冲突。 由于输入空间无限而输出空间有限,必然存在不同输入产生相同哈希值的情况。这在哈希表中会影响性能,在安全场景中可能被恶意利用。
  • 第二是不可逆性带来的局限。 虽然单向性是安全优势,但也意味着数据丢失后无法恢复。比如忘记密码只能重置,不能找回。
  • 第三是某些哈希算法存在安全漏洞。 比如MD5已经被证明容易产生碰撞,不再适合安全要求高的场景。
  • 第四是计算开销。 复杂的哈希算法计算成本较高,在高并发场景下可能影响性能。

为了解决这些问题,我们通常会选择合适的哈希算法,使用加盐等技术增强安全性,或者采用一致性哈希等改进方案。

React相关 - 虚拟DOM

面试官: "虚拟DOM是什么?为什么要用它?"

这个问题我准备得比较充分:

"虚拟DOM是用JavaScript对象描述真实DOM结构的技术:

解决的问题

  • DOM操作成本高,频繁操作会导致重排重绘
  • 直接操作DOM难以进行性能优化
  • 跨平台开发需要抽象层

实现原理

  • 用JS对象树描述UI结构
  • 状态变化时生成新的虚拟DOM树
  • 通过diff算法比较新旧树的差异
  • 只更新有变化的真实DOM节点

性能优势

  • 批量更新,减少DOM操作次数
  • diff算法优化,避免不必要的更新
  • 可以在内存中进行大量计算

不过虚拟DOM也不是银弹, 它也有一些局限性: 首先是增加了内存开销,因为要维护一套虚拟DOM树;其次对于简单的DOM操作,虚拟DOM的diff过程可能反而增加了计算成本;另外在某些场景下,比如大量静态内容,虚拟DOM的优势并不明显。"

关于AI的使用

面试官: "现在AI这么火,你平常怎么使用AI的?"

这个问题挺有意思,我实话实说:

"我主要在这几个场景用AI:

代码编写辅助

  • 使用Cursor,Trae等辅助开发,提高编程效率
  • 写一些工具函数,比如日期处理、数据格式化
  • 生成单元测试用例
  • 解释复杂的正则表达式或算法逻辑

学习新技术

  • 快速了解新框架的核心概念
  • 解释代码报错的原因
  • 对比不同技术方案的优劣

日常开发

  • 写技术文档的大纲
  • 生成一些样板代码,如API接口定义
  • 帮助review代码,找出潜在问题

但我不会完全依赖AI,特别是核心业务逻辑,还是需要自己深入理解和编写。"

面试官: "那你觉得AI写的代码有什么弊端?"

"根据我的使用经验,主要有这些问题:

缺乏上下文理解

  • AI不了解项目的具体架构和约定
  • 可能生成与现有代码风格不一致的代码
  • 无法考虑业务的特殊需求和边界条件

质量问题

  • 有时逻辑看似正确,但存在潜在bug
  • 可能过度设计或选择不合适的实现方案
  • 错误处理常常不够完善

依赖性风险

  • 过度依赖可能导致思维惰性
  • 遇到复杂问题时缺乏独立解决能力
  • 对生成的代码理解不够深入

安全隐患

  • 可能包含有安全漏洞的代码模式
  • 对于敏感数据处理不够谨慎

所以我的原则是:AI辅助但不替代,生成的代码一定要仔细review,核心逻辑还是要自己来实现。"

面试结果与反思

整个面试持续了半个小时,虽然大部分问题都有理解过,但是一旦表述就有点语无伦次,我觉得有几个地方还需要准备开始面试的同学注意一下:

  1. 还是要多面试,克服紧张问题。
  2. 多练一练表述,以防会但是表达不行。
  3. 基础一定要扎实,CSS、JS的细节很重要。

总的来说,这次面试涵盖面挺广的,从基础的CSS、JS到工程化、数据结构,再到新兴的AI应用,确实考验了一个前端工程师的综合素质。

希望这份面坤对大家有帮助!