秋招面试——近期面试真题小结(1.4w字总结,干货满满,建议收藏起来反复观看)

166 阅读18分钟

最近不是差不多秋招提前批了吗?我就去面了几家,我把我面试的题目分享并且试着解析给jym。希望对jym有所帮助(●'◡'●)

秋招面试题

移动端项目和web项目区别

这道题是道比较开放的题目,也是我最开始被问到的几道题目之一,说实话我当时回答的不知所云,哈哈。

后来查资料,我发现,这道题关键是要讲的是核心的差异。

示例回答:

移动端和 Web 项目的核心区别在于运行环境交付方式
移动端 App 运行在 iOS/Android,需要打包和发布,体验更流畅,也能调用系统硬件能力,但更新成本相对高;Web 项目则运行在浏览器中,不需要安装,迭代快,维护成本低,但受限于浏览器,性能和交互会差一些。
通常,用户高频使用、需要硬件支持的场景更适合做移动端,而信息展示、后台管理这类项目用 Web 就更高效。
在实际项目里,我也接触过跨平台方案,比如 React Native、Flutter 或者 PWA,用来平衡体验和开发效率。

介绍hooks组件

面试里如果让你 介绍 Hooks 组件,他们通常想听到:

  1. 你是否理解 React Hooks 的核心概念。
  2. 你能否说明 Hooks 相对 class 组件的优势。
  3. 你能否举例说明常用的 Hooks。

面试思路

  1. 先解释 Hooks 是什么

React Hooks 是 React 16.8 引入的一种新特性,让函数组件可以使用 state、生命周期以及其他 React 特性,而不必写 class 组件。

  1. 说明为什么有 Hooks(解决了什么问题)
  • 复用逻辑更方便:class 组件里逻辑复用需要 mixin 或 HOC,比较复杂;Hooks 可以通过自定义 Hook 来抽离逻辑。
  • 代码更简洁:函数式编程风格更直观,避免了 this 绑定问题。
  • 状态逻辑更清晰:相关逻辑可以写在一起,不像 class 组件里分散在不同生命周期方法中。
  1. 常用的内置 Hooks
  • useState:在函数组件中添加状态。
  • useEffect:处理副作用(如请求数据、订阅事件、操作 DOM)。
  • useContext:共享上下文数据。
  • useReducer:更复杂的 state 管理(类似 Redux)。
  • useMemo / useCallback:性能优化,避免不必要的渲染或函数重新创建。
  • useRef:获取 DOM 或保存跨渲染周期的变量。
  1. 掌握主动权

你可以顺势介绍useRef的相关的知识点,比如受控组件和非受控组件啊,useMemo再扯到懒加载啊。或者讲到react 16引进的函数组件、类组件的区别啊,或者react 类组件的生命周期啊等等......

  1. 最后总结

Hooks 让函数组件拥有状态和生命周期,更容易复用逻辑、代码更简洁,已经逐渐取代 class 组件成为主流开发方式。这里也可以提一嘴自定义hooks,加分项哦!

面试要掌握主动权

react 和 vue 相比有什么区别

React 和 Vue 的主要区别在于设计理念和数据绑定。
React 更像一个 UI 库,强调函数式编程和单向数据流,灵活度高,但需要搭配社区库;Vue 是渐进式框架,自带路由、状态管理,双向绑定写法更直观,上手快。
在性能上,React 需要手动做一些优化(如 memo、useCallback),而 Vue 通过响应式系统可以更自动化地追踪依赖。
如果是大型复杂项目,React 更适合;如果是中小型快速开发,Vue 更高效。

从响应式方面来回答的话:

响应式的核心思想是数据驱动视图,数据变化时视图会自动更新。
Vue2 是基于 Object.defineProperty 的 getter/setter 实现响应式,Vue3 改用 Proxy,更完善地支持对象新增、删除和数组变化。Vue 的响应式是依赖追踪的,某个数据变化,只会触发相关视图更新。
React 的实现方式不同,它不是自动依赖追踪,而是通过状态驱动:当 setStateuseState 更新时,整个组件重新渲染,再通过虚拟 DOM diff 更新需要变化的部分。
所以 Vue 的更新粒度更细,而 React 需要配合 memouseCallback 等优化。

你了解es6这个框架吗

这里要注意一点哈 🙂,ES6 不是一个框架,而是 ECMAScript 2015 的简称,是 JavaScript 的一个版本标准。

很多面试官会故意这样问,想考你是否能分清楚 语言标准 vs 框架/库
如果你直接说 “ES6 不是框架”,反而能显得你很专业。

首先澄清一下,ES6 并不是框架,它是 ECMAScript 2015,是 JavaScript 的一个重要版本更新,引入了很多新的语法和特性,极大地提升了开发效率和代码可维护性。

在 ES6 中,比较核心和常用的特性包括:

  • 变量声明letconst,解决了 var 没有块级作用域、变量提升等问题。
  • 箭头函数:简化了函数写法,并且不会改变 this 指向,常用于回调函数。
  • 模板字符串:用反引号 `,支持多行字符串和字符串插值,让字符串拼接更直观。
  • 解构赋值 & 扩展运算符:快速提取对象/数组里的数据,或者用 ... 实现数组/对象的合并、浅拷贝。
  • 模块化:引入了 importexport,让前端代码可以更清晰地组织,替代了早期的全局变量或 CommonJS。
  • Promise:用于更优雅地处理异步逻辑,也为后续的 async/await 奠定了基础。
  • 新增数据结构:比如 MapSet,解决了传统对象和数组在某些场景下的局限性。

总结来说,ES6 不是框架,而是 JavaScript 的一次标准升级。它带来的模块化、箭头函数、解构赋值、Promise 等特性,让代码更简洁、更易读,也为现代前端框架(比如 React、Vue、Angular)的发展提供了语言层面的支持。

然后面试官就问我了: es6的展开运算符是深拷贝还是浅拷贝?

es6的展开运算符是深拷贝还是浅拷贝

这个时候你可以先解释一遍概念,什么是深拷贝,什么是浅拷贝。一方面让面试官认为你对概念非常清晰,加分,一方面又留下了时间给你回忆思考知识点。嘛,最重要的是增添面试时长哈哈。

面试官,我先来介绍一下深拷贝和浅拷贝吧。深拷贝和浅拷贝的本质区别在于 拷贝的“层级”

  • 浅拷贝只拷贝对象的 第一层属性,内部引用类型仍共享。
  • 深拷贝会递归拷贝 所有层级,内部对象完全独立。

浅拷贝(Shallow Copy)

  • 拷贝对象的第一层属性。

  • 基本类型拷贝值,引用类型拷贝地址。

  • 常用方法:

    • 对象:Object.assign({}, obj){ ...obj }
    • 数组:slice()concat()[...arr] 这里就顺势说了...展开运算符是浅拷贝了

深拷贝(Deep Copy)

  • 拷贝对象的 所有层级属性,内部引用类型也会复制一份独立副本。

  • 常用方法:

    • JSON.parse(JSON.stringify(obj))(加分点:函数、undefined、Symbol、循环引用会丢失)

这里说完JSON.parse丢失的问题后,可以提出我们自己的自定义深拷贝,这里展示代码给你们看:

function clone(target, map = new WeakMap()) {
    if (typeof target === 'object') {
        let cloneTarget = Array.isArray(target)?[]:{};
        if (map.get(target)) {
            return map.get(target);
        }
        map.set(target, cloneTarget);
        for (const key in target) {
            cloneTarget[key] = clone(target[key], map);
        }
        
        return cloneTarget;
    } else {
        return target;
    }
}

for of 循环有了解吗?

面试时如果被问到 for…of 循环,可以先解释它的 作用、特点、与其他循环的区别

for…of 是 ES6 新增的循环语法,用来遍历 可迭代对象,如数组、字符串、Set、Map 等。
for…in 不同,它遍历的是 元素的值 而不是键名,也比传统 for 循环更简洁。
在实际开发中,我常用它遍历数组、Set 或 Map,代码可读性和可维护性都更好。

如果你想要掌握面试主动权,拉长面试时间。

其实我们可以从可迭代对象这里扯到属性描述符。

JavaScript 对象的每个属性都有一个 属性描述符(Property Descriptor) ,用来控制属性的行为,包括:

  • writable:是否可写
  • enumerable:是否可枚举
  • configurable:是否可配置
  • valueget/set:属性的值或访问器函数
    可以用 Object.getOwnPropertyDescriptor(obj, 'prop') 查看,也可以用 Object.defineProperty 来设置。

promise 回调地狱问题如何去解决

这是前端面试中非常经典的问题,重点是 理解回调地狱的本质 + 掌握 Promise/async-await 的解决方案

面试回答思路

什么是回调地狱

回调地狱(Callback Hell)是指异步操作层层嵌套回调函数,导致代码可读性差、维护困难、错误处理复杂的现象。

示例:

doSomething(function(result1) {
  doSomethingElse(result1, function(result2) {
    doThirdThing(result2, function(result3) {
      console.log(result3);
    });
  });
});

Promise 的解决方案

  • Promise 将异步操作封装成对象,使用 .then() 链式调用,避免回调嵌套。
doSomething()
  .then(result1 => doSomethingElse(result1))
  .then(result2 => doThirdThing(result2))
  .then(result3 => console.log(result3))
  .catch(err => console.error(err));

优点:

  • 链式调用,平坦化代码结构
  • .catch() 可以统一处理错误

async/await 的解决方案(更现代)

  • ES8 引入 async/await,可以用同步风格写异步代码,极大提高可读性
async function main() {
  try {
    const result1 = await doSomething();
    const result2 = await doSomethingElse(result1);
    const result3 = await doThirdThing(result2);
    console.log(result3);
  } catch (err) {
    console.error(err);
  }
}
main();

优点:

  • 代码像同步流程,可读性强
  • 错误统一用 try/catch 捕获
  • 与 Promise 完全兼容

面试回答示例

回调地狱是异步操作层层嵌套回调导致的可读性差、维护困难的问题。
解决方案:

  1. Promise:通过 .then() 链式调用平铺回调,同时 .catch() 统一错误处理。
  2. async/await:将异步操作写成同步风格,用 await 获取结果,try/catch 统一处理异常,可读性更好。
    这两种方式都可以避免回调地狱,提高代码可维护性。

async 和 await

asyncawait 是在ES2017(也叫 ES8) 中引入的。

async 用于声明异步函数,返回 Promise;
await 用于等待 Promise 结果,使异步代码像同步流程一样写,提升可读性。
错误处理用 try/catch.catch()
并行异步处理可以用 Promise.all(),串行则直接 await 多个操作。
async/await 本质上不会阻塞主线程,是基于 Promise 的语法糖。

这里可以扩展promise.allpromise.race,可以讲event-loop事件循环机制。

日常开发过程中你用过哪些组件库

这里可以讲UI组件库:ant design, react-vant

还有hooks组件库:阿里的ahooks组件库

css怎么实现居中

这里居中就可以讲很久了。如果你对居中非常熟悉,可以拆分成

  • 行内元素
    • 水平居中
    • 垂直居中
  • 块级元素
    • 固定宽高块级盒子水平垂直居中
    • 不固定宽高块级盒子水平垂直居中

行内元素

行内元素都比较简单,就简单带过吧。

水平居中

采用text-align

text-align: center;
单行文本垂直居中

单行文本可用 line-height 等于容器高度

height: 50px;
line-height: 50px;

块级元素

固定宽高块级盒子水平垂直居中

适用场景:父元素已知高度,子元素宽高固定。

  1. absolute + margin 负值 缺点需要知道盒子宽高
  2. absolute + margin auto (重要)
  3. absolute + calc (css calc 计算函数) 缺点是性能差,很少用
absolute + margin 负值
.child {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200px;
  height: 100px;
  margin-left: -100px; /* 宽度的一半 */
  margin-top: -50px;   /* 高度的一半 */
}
  • 原理:先用 top/left: 50% 把元素左上角移动到父元素中心,再用负 margin 把中心点对齐。

  • 缺点:必须知道宽高,否则无法正确居中。

absolute + margin auto
.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 0; bottom: 0; left: 0; right: 0;
  width: 200px;
  height: 100px;
  margin: auto;
}
  • 原理:设置 top/right/bottom/left: 0 并且 margin: auto,块元素会在父容器中居中。

  • 优点:无需计算负值,推荐使用。

  • 缺点:仍需知道宽高才能居中。

absolute + calc()
.child {
  position: absolute;
  width: 200px;
  height: 100px;
  top: calc(50% - 50px);   /* 50% - 高度一半 */
  left: calc(50% - 100px); /* 50% - 宽度一半 */
}
  • 原理:用 calc() 计算偏移量,使元素中心对齐父容器中心。

  • 缺点:性能稍差,代码可读性不如 margin auto,现代开发很少用。

不固定宽高块级盒子水平垂直居中

适用场景:子元素宽高不确定,或者内容自适应。

absolute + transform
.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
  • 原理:top/left 移动到父元素中心,translate(-50%, -50%) 把元素自身中心移动到该位置。

  • 优点:不需要知道宽高,通用性强。

line-height + vertical-align
.parent {
  height: 100px;
  line-height: 100px; /* 与高度相等 */
  text-align: center;
}
.child {
  display: inline-block;
  vertical-align: middle;
}
  • 原理:利用行高让单行文本或行内块元素垂直居中。

  • 缺点:只能用于单行文本或 inline-block 元素,不适合多行内容。

writing-mode
  • 改变文本流方向,让垂直方向变为水平方向,然后再用 text-align 居中。

  • 应用场景:文本或特殊布局,不常用,属于 CSS 冷门技巧。

.parent {
  writing-mode: vertical-rl;
  text-align: center;
}

注意:

  • writing-mode 不是行内元素居中方案

  • 它作用于 块级元素(容器),通过改变文本方向间接影响内容居中

  • 面试讲时可以说:

    writing-mode 是改变块级文本书写方向的属性,可以配合 text-align 实现居中,但属于冷门方法,不推荐做一般居中使用

table-cell
.parent {
  display: table;
  width: 100%;
  height: 200px;
}
.child {
  display: table-cell;
  vertical-align: middle;
  text-align: center;
}
  • 原理:利用 table-cell 的 vertical-align: middle 属性实现垂直居中。

  • 优点:兼容旧浏览器

  • 缺点:语义上不太直观,不推荐现代开发使用。

Flex 布局(现代推荐)
.parent {
  display: flex;
  justify-content: center; /* 水平居中 */
  align-items: center;     /* 垂直居中 */
}

优点

  • 简洁易懂
  • 支持多行多元素
  • 不依赖子元素宽高
Grid 布局(现代推荐)
.parent {
  display: grid;
  place-items: center; /* 同时水平+垂直居中 */
}

优点

  • 语义清晰
  • 代码最简洁
  • 支持复杂布局

恭喜你一口气看到了这里呀,如果累了,记得休息一下,其实我也敲累了😵‍💫

OIP.jpg

ok, 整顿完毕,让我们一口气干完,gogogo。

你了解过一些响应式设计的方法吗

在 React 的语境下,如果面试官问“响应式设计的方法”,你可以把回答分成 两层含义

  • CSS 级响应式(传统前端响应式设计)

让页面在不同设备、屏幕尺寸下自适应显示,保证良好的用户体验。

常用方法:

  1. 流式布局(Fluid Layout) :使用百分比或相对单位代替固定宽度
  2. 媒体查询(Media Queries) :根据屏幕宽度或设备特性调整样式,下一个题目就是关于这个。
  3. Flex 布局:自适应排列、换行
  4. Grid 布局:二维自适应栅格布局
  5. 相对单位em/rem/vw/vh
  6. 响应式图片max-width: 100%
  7. 移动优先设计(Mobile First) :从小屏设计,逐步增强大屏布局 可以和相对单位结合起来讲,再衍生到去除双击放大:user-scalable=no,移动端字体适配:font-family: -apple-system, BlinkMacSystemFont...,最后可以讲组件库lib-flexible和postcss-pxtorem
  • React 级响应式(数据驱动 UI) UI 会根据数据变化自动更新,无需手动操作 DOM,体现“响应式”概念。

常用方法:

  1. State / Props:组件状态或属性变化自动触发视图更新
  2. Hooks:如 useState, useReducer 管理状态,实现响应式效果
  3. 自定义 Hooks / 状态管理库:如 useStore 或 Redux,使数据变化驱动 UI
  4. 响应式布局库:结合 CSS 或 UI 组件库(如 AntD Grid / Flex)实现布局自适应

媒体查询

媒体查询(Media Query) 是 CSS3 提供的一种功能,允许根据设备的不同特性(如屏幕宽度、高度、分辨率、方向等)应用不同的样式,从而实现响应式设计。

👉 简单理解: “条件判断 + 样式规则” ,不同设备满足不同条件时会加载不同的 CSS。

@media mediatype and (condition) {
  /* 样式 */
}
  • mediatype:媒体类型,例如:

    • all:所有设备(默认)
    • screen:屏幕
    • print:打印机
  • (condition):条件表达式,例如:

    • min-width:最小宽度
    • max-width:最大宽度
    • orientation:方向(portrait 竖屏 / landscape 横屏)
    • resolution:分辨率
  • 常用条件写法

  1. 按屏幕宽度
/* 手机(最大宽度 600px) */
@media screen and (max-width: 600px) {
  body {
    background: lightblue;
  }
}

/* 平板(宽度 601px ~ 1024px) */
@media screen and (min-width: 601px) and (max-width: 1024px) {
  body {
    background: lightgreen;
  }
}

/* PC(大于 1024px) */
@media screen and (min-width: 1025px) {
  body {
    background: lightyellow;
  }
}
  1. 按方向
/* 竖屏 */
@media screen and (orientation: portrait) {
  body {
    font-size: 14px;
  }
}

/* 横屏 */
@media screen and (orientation: landscape) {
  body {
    font-size: 18px;
  }
}
  1. 按分辨率
/* 针对高清屏幕(Retina) */
@media screen and (min-resolution: 2dppx) {
  img {
    content: url("image@2x.png");
  }
}

面试回答示例

媒体查询是 CSS3 提供的一种根据设备特性(比如宽度、高度、分辨率、方向等)应用不同样式的技术,是响应式设计的核心手段。
常见写法是基于屏幕宽度的 min-widthmax-width,比如移动端小于 600px 用一套样式,PC 大于 1024px 用另一套样式。
它的优点是性能好、原生支持,缺点是规则维护复杂。

可以顺势讲出tailwindcss,可以自适应设备样式。

git 方面的命令以及考点

Git 基本命令

  • 初始化和配置
git clone                # 克隆远程仓库
git init                 # 初始化仓库
git config --global user.name "你的名字"    
git config --global user.email "你的邮箱"

其中要注意

git config --global user.name "你的名字"

git config --global user.email "你的邮箱"

Git 配置用户信息 的核心命令,用于标识是谁在操作仓库。毕竟入职可能第一件事就是配置这个(●'◡'●)。

  • 查看状态和历史
git status               # 查看当前状态
git log --oneline        # 查看提交历史
  • 文件操作
git add file.txt         # 将文件添加到暂存区
git commit -m "说明"     # 提交到本地仓库
  • 分支操作
git branch               # 查看分支
git branch dev           # 创建分支 dev
git checkout dev         # 切换dev分支
git checkout -b dev      # 创建并切换到 dev
git merge dev            # 合并分支(当前分支和dev分支合并)
git branch -d dev        # 删除dev分支
  • 远程操作
git remote add origin url       # 添加远程仓库
git remote -v                   # 查看远程仓库
git push origin main            # 推送到远程
git pull origin main            # 拉取远程更新并合并
git fetch origin                # 获取远程更新(不合并)
  • 回退与撤销
git reset file.txt           # 清空单个文件的暂存
git reset                    # 清空所有文件的暂存
git reset --soft HEAD^       # 回退到上一个提交(修改内容保留在暂存区)
git reset --mixed HEAD^      # 回退到上一个提交(修改退回到工作区)
git reset --hard HEAD^       # 修改和暂存的内容都丢弃
git reset --hard <commit_id> # 回退到指定 commit,可以不用hard,用soft,mixed都行
git revert <commit_id>       # 安全回退,历史不会丢失,适合多人协作

Git 常考点

  • Git 工作区、暂存区、版本库
    • 工作区(Working Directory) :写代码的地方
    • 暂存区(Staging Area) :保存即将提交的快照
    • 版本库(Repository) :真正存储历史的地方

常见问题:git add 是把改动从工作区放到暂存区,git commit 是把暂存区的内容提交到版本库。

  • Git reset vs revert

    • reset:直接修改历史,可能导致冲突,不推荐在共享分支使用
    • revert:生成一个新的提交,抵消之前的提交,更安全
  • merge vs rebase

    • merge:保留历史分叉,生成一个合并节点(多一条分支记录)
    • rebase:把提交“搬运”到目标分支上,历史更干净,但容易改写历史
  • pull vs fetch

    • git fetch:只下载远程最新内容,本地不变
    • git pull:等于 fetch + merge,会合并到本地
  • HEAD、HEAD^、HEAD~3

    • HEAD:当前最新提交
    • HEAD^:上一个提交
    • HEAD~3:往上数第 3 个提交

常见场景题

  1. 撤销一次提交但保留代码
    git reset --soft HEAD^

  2. 本地误删文件,想恢复
    git checkout -- file.txt

    注意: checkout 既能切换分支,又能恢复文件,这其实是git的历史遗留问题。

    从 Git 2.23 开始,Git 官方把 checkout 拆成了两个更清晰的命令:

    • git switch → 专门用于切换分支
    • git restore → 专门用于恢复文件
  3. 代码写一半临时切分支
    git stash 保存,切分支,git stash pop 恢复

  4. 在团队中你和团队的其他一个小伙伴同时修改了一个代码文件,直接push可能提示冲突,怎么解决
    → 这里就讲其中的一种方法。先拉取远程最新代码

git pull origin main
  • 如果没有冲突,Git 会自动合并,然后你再 git push 就行。
  • 如果有冲突,Git 会在文件中标记冲突部分
<<<<<<< HEAD
// 你的修改
=======
// 同事的修改
>>>>>>> origin/main

手动解决冲突

  • 编辑文件,把 <<<<<<<、=======、>>>>>>> 这些标记去掉
  • 保留你想要的正确代码(可能是保留一方,也可能是合并两边逻辑)

标记冲突已解决并提交

git add app.js
git commit -m "fix: 修复在app.js中的冲突"

再推送到远程仓库

git push origin main

恭喜你,看完了。面试加油呀