创见博客
字节前端一面(2.27 15:00)
七崽爱吃小饼干2026/03/02阅读 0专栏 前端面经
codeType
1. 自我介绍
2. mobx和Redux的区别,分别适用什么场景
3. flex布局,怎么用flex布局实现指定宽度区间一行有多少卡片,卡片间距固定。
4. 跨域
5. 强缓存和协商缓存
6. 看一段代码,给输出
7. 根据路径把原对象的值给目标对象
8. 最长无重复子串
9. ai开发有哪些实践
10. 介绍盒模型
11. 由margin塌陷介绍BFC

1. mobx和redux的区别

  • 设计理念:Redux 是函数式、单向数据流、不可变数据;MobX 是响应式、观察者模式、可变数据。
  • 使用方式:Redux 需 action/reducer/dispatch,模板代码多;MobX 直接修改状态,代码简洁。
  • 适用场景:Redux 适合大型复杂应用、强规范场景;MobX 适合中小型应用、追求开发效率的场景。

总结

  • 核心差异:Redux 重「流程和可预测性」,MobX 重「简洁和响应式」,前者是函数式思想,后者是面向对象 + 响应式思想。
  • 开发选择:大型 / 复杂应用、团队协作优先 Redux;中小型应用、快速开发优先 MobX。
  • 面试关键:不用死记硬背,抓住「不可变 vs 可变」「单向数据流 vs 响应式」两个核心点展开即可。

2. flex布局

flex布局

基于 Flex 布局的响应式自适应卡片布局

Flex 是 CSS 里的弹性布局模型,核心就是:给父容器设置 display:flex,让它的直接子元素变成「弹性项目」,从而轻松实现各种排列、对齐、自适应布局。

一般从三个层面来介绍和使用:

1. 核心思想

  • 它是一套弹性盒子模型,由 Flex 容器 和 Flex 项目 组成。
  • 容器内有两条轴线:主轴和交叉轴,所有排列、对齐都围绕这两条轴来控制。
  • 目标就是:快速、简洁、可靠地实现垂直居中、均分、自适应、两端对齐等传统布局很难做的场景。

2. 容器的作用(控制整体布局)

容器主要控制项目怎么排、怎么对齐、换不换行:

  • flex-direction:控制主轴方向(水平/垂直)
  • flex-wrap:控制是否换行
  • justify-content:主轴对齐(水平居中、两端对齐、均分)
  • align-items:交叉轴对齐(垂直居中、顶部、底部)
  • align-content:多行时的交叉轴对齐

我最常用的就是:

css
display: flex;
justify-content: center;
align-items: center;

一行实现水平垂直居中。


3. 项目的作用(控制单个子元素)

项目主要控制自己占多少空间、怎么伸缩:

  • flex-grow:有剩余空间时,要不要放大
  • flex-shrink:空间不够时,要不要缩小
  • flex-basis:基准宽度
  • 简写:flex:1 最常用,表示占满剩余空间

实际开发里我经常用:

  • flex:1 自适应填满
  • flex:0 0 固定值 固定宽度/高度
  • align-self 单独控制某个项目的对齐

4. 弹性项目的特点(面试加分点)

变成弹性项目后会有几个关键特性:

  1. 默认沿主轴水平排列,不再独占一行
  2. float、clear、vertical-align 失效
  3. 可以直接设置宽高,行内元素也会变成块级
  4. 支持 margin:auto 自动填充剩余空间
  5. 空间不足会自动收缩,不会轻易溢出

5. 我在实际项目中的使用

我在项目里主要用 Flex 做:

  • 水平垂直居中
  • 左侧固定、右侧自适应
  • 导航栏、宫格、卡片列表均分
  • 表单行内对齐
  • 移动端各种弹性适配

它比传统 float / position 更简洁、更稳定、更少兼容问题。


超精简版(30 秒回答)

Flex 是弹性布局,给父元素设置 display:flex,子元素就变成弹性项目,可以通过主轴和交叉轴轻松控制排列、对齐、分配空间。 容器用 justify-content 和 align-items 实现居中对齐,项目用 flex:1 实现自适应。 它能非常方便地实现垂直居中、均分、自适应布局,是我日常开发最主要的布局方案。

3. 跨域

前端跨域

  1. 什么是同源策略
  2. 跨域主要限制的三个方面:ajax请求、dom操作、浏览器存储(localStorage、sessionStorage和Cookie等)
  3. 怎么解决跨域:本地开发(代理服务器)以及生产环境(CORS、JSONP、img)

4. 强缓存与协商缓存

强缓存与协商缓存

  1. 什么是http缓存
  2. 强缓存基于过期时间Expired 或者 有效时间Cache-Control(新标准)。不发起请求,直接从本地获取
  3. 协商缓存 基于ETag或者资源最后修改时间进行比较,比较发生在服务端

5. 看代码给输出(闭包陷阱以及事件循环)

js
for(var i = 0; i < 3; i++){
    setTimeout(() => {
        console.log(i)
    }, 0)
    console.log(i)
}

输出

codeType
0
1
2
3
3
3

逐行拆解执行逻辑(核心:var的全局作用域 + 事件循环)

要理解结果,必须理清同步代码执行顺序和异步任务(setTimeout)执行时机,以及 var 声明变量的特性:

1. var 声明的变量特性

var i 声明的变量是函数级作用域/全局作用域(而非块级作用域),整个代码中只有一个 i 变量,循环中每次修改的都是同一个 i。

2. 事件循环机制

setTimeout 是宏任务,即使延迟设为 0,也不会立即执行,而是会被加入「宏任务队列」,等待同步代码全部执行完毕后,再执行队列中的回调。

3. 分步执行过程

javascript
for(var i = 0; i < 3; i++){
    // 步骤1:每次循环先创建setTimeout回调(暂不执行),加入宏任务队列
    setTimeout(() => {
        console.log(i) // 回调执行时,取的是最终的i值
    }, 0)
    // 步骤2:同步代码,立即执行console.log(i)
    console.log(i)
}

具体执行步骤:

  • 第1次循环(i=0):
    • 创建 setTimeout 回调(暂存)→ 加入宏任务队列;
    • 执行同步代码 console.log(i) → 输出 0;
    • i 自增 → i=1。
  • 第2次循环(i=1):
    • 创建 setTimeout 回调(暂存)→ 加入宏任务队列;
    • 执行同步代码 console.log(i) → 输出 1;
    • i 自增 → i=2。
  • 第3次循环(i=2):
    • 创建 setTimeout 回调(暂存)→ 加入宏任务队列;
    • 执行同步代码 console.log(i) → 输出 2;
    • i 自增 → i=3(此时 i=3 不满足 i<3,循环结束)。
  • 同步代码执行完毕,开始执行宏任务队列中的3个 setTimeout 回调:
    • 第一个回调执行:console.log(i) → i=3 → 输出 3;
    • 第二个回调执行:console.log(i) → i=3 → 输出 3;
    • 第三个回调执行:console.log(i) → i=3 → 输出 3。

最终输出顺序:0 → 1 → 2 → 3 → 3 → 3。

如何修改代码,让输出变成 0 1 2 0 1 2?

如果希望 setTimeout 中输出循环当时的 i 值,有3种常见方案(面试常问优化方式):

方案1:用 let 替代 var(推荐,ES6 方案)

let 是块级作用域,每次循环会创建独立的 i 变量,回调会捕获当前循环的 i 值:

javascript
for(let i = 0; i < 3; i++){ // 改为let
    setTimeout(() => {
        console.log(i) // 捕获当前循环的i:0、1、2
    }, 0)
    console.log(i) // 同步输出:0、1、2
}
// 输出:0 1 2 0 1 2

方案2:利用立即执行函数(IIFE)创建闭包(ES5 方案)

通过闭包保存每次循环的 i 值,形成独立作用域:

javascript
for(var i = 0; i < 3; i++){
    (function(j) { // 形参j接收当前i值
        setTimeout(() => {
            console.log(j) // 访问闭包中的j:0、1、2
        }, 0)
    })(i) // 实参传入当前i值
    console.log(i) // 同步输出:0、1、2
}
// 输出:0 1 2 0 1 2

方案3:setTimeout 传第三个参数(简洁方案)

setTimeout 支持传入额外参数,作为回调的入参:

javascript
for(var i = 0; i < 3; i++){
    setTimeout((j) => {
        console.log(j) // 接收第三个参数j:0、1、2
    }, 0, i) // 第三个参数传入当前i值
    console.log(i) // 同步输出:0、1、2
}
// 输出:0 1 2 0 1 2

总结

  1. 核心结果:原代码输出 0 1 2 3 3 3,核心原因是 var 的全局作用域 + setTimeout 异步执行;
  2. 关键原理:var 无块级作用域,循环中只有一个 i 变量,异步回调执行时 i 已变为3;
  3. 优化方案:用 let(块级作用域)、IIFE 闭包、setTimeout 额外参数,均可让异步回调捕获当前循环的 i 值。

6. 代码题,根据路径将source对象的指定值给target的指定路径

  • 支持点分隔的路径(如 a.b.c),也支持数组下标(如 list[0].name);
  • 若 target 中路径对应的层级不存在,自动创建嵌套对象 / 数组;
  • 兼容空路径、不存在的路径等边界情况;
  • 函数入参:source 源对象、sourcePath 源路径、target 目标对象、targetPath 目标路径。

实现思路

  • 路径解析:把路径(如 a.b[0].c)解析成数组(如 ['a', 'b', '0', 'c']);
  • 取值逻辑:根据解析后的路径,从 source 中获取对应值;
  • 赋值逻辑:根据解析后的目标路径,把值赋值到 target 对应位置(不存在的层级自动创建)。

7. 代码题:无重复字符的最长子串

无重复字符的最长子串

滑动窗口+哈希表解决

8.盒模型

盒模型

评论
0/100