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 是 CSS 里的弹性布局模型,核心就是:给父容器设置 display:flex,让它的直接子元素变成「弹性项目」,从而轻松实现各种排列、对齐、自适应布局。
一般从三个层面来介绍和使用:
1. 核心思想
- 它是一套弹性盒子模型,由 Flex 容器 和 Flex 项目 组成。
- 容器内有两条轴线:主轴和交叉轴,所有排列、对齐都围绕这两条轴来控制。
- 目标就是:快速、简洁、可靠地实现垂直居中、均分、自适应、两端对齐等传统布局很难做的场景。
2. 容器的作用(控制整体布局)
容器主要控制项目怎么排、怎么对齐、换不换行:
flex-direction:控制主轴方向(水平/垂直)flex-wrap:控制是否换行justify-content:主轴对齐(水平居中、两端对齐、均分)align-items:交叉轴对齐(垂直居中、顶部、底部)align-content:多行时的交叉轴对齐
我最常用的就是:
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. 弹性项目的特点(面试加分点)
变成弹性项目后会有几个关键特性:
- 默认沿主轴水平排列,不再独占一行
- float、clear、vertical-align 失效
- 可以直接设置宽高,行内元素也会变成块级
- 支持
margin:auto自动填充剩余空间 - 空间不足会自动收缩,不会轻易溢出
5. 我在实际项目中的使用
我在项目里主要用 Flex 做:
- 水平垂直居中
- 左侧固定、右侧自适应
- 导航栏、宫格、卡片列表均分
- 表单行内对齐
- 移动端各种弹性适配
它比传统 float / position 更简洁、更稳定、更少兼容问题。
超精简版(30 秒回答)
Flex 是弹性布局,给父元素设置
display:flex,子元素就变成弹性项目,可以通过主轴和交叉轴轻松控制排列、对齐、分配空间。 容器用justify-content和align-items实现居中对齐,项目用flex:1实现自适应。 它能非常方便地实现垂直居中、均分、自适应布局,是我日常开发最主要的布局方案。
3. 跨域
- 什么是同源策略
- 跨域主要限制的三个方面:ajax请求、dom操作、浏览器存储(localStorage、sessionStorage和Cookie等)
- 怎么解决跨域:本地开发(代理服务器)以及生产环境(CORS、JSONP、img)
4. 强缓存与协商缓存
- 什么是http缓存
- 强缓存基于过期时间Expired 或者 有效时间Cache-Control(新标准)。不发起请求,直接从本地获取
- 协商缓存 基于ETag或者资源最后修改时间进行比较,比较发生在服务端
5. 看代码给输出(闭包陷阱以及事件循环)
for(var i = 0; i < 3; i++){
setTimeout(() => {
console.log(i)
}, 0)
console.log(i)
}
输出
0
1
2
3
3
3
逐行拆解执行逻辑(核心:var的全局作用域 + 事件循环)
要理解结果,必须理清同步代码执行顺序和异步任务(setTimeout)执行时机,以及 var 声明变量的特性:
1. var 声明的变量特性
var i 声明的变量是函数级作用域/全局作用域(而非块级作用域),整个代码中只有一个 i 变量,循环中每次修改的都是同一个 i。
2. 事件循环机制
setTimeout 是宏任务,即使延迟设为 0,也不会立即执行,而是会被加入「宏任务队列」,等待同步代码全部执行完毕后,再执行队列中的回调。
3. 分步执行过程
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。
- 第一个回调执行:console.log(i) → i=3 → 输出
最终输出顺序:0 → 1 → 2 → 3 → 3 → 3。
如何修改代码,让输出变成 0 1 2 0 1 2?
如果希望 setTimeout 中输出循环当时的 i 值,有3种常见方案(面试常问优化方式):
方案1:用 let 替代 var(推荐,ES6 方案)
let 是块级作用域,每次循环会创建独立的 i 变量,回调会捕获当前循环的 i 值:
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 值,形成独立作用域:
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 支持传入额外参数,作为回调的入参:
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
总结
- 核心结果:原代码输出
0 1 2 3 3 3,核心原因是var的全局作用域 + setTimeout 异步执行; - 关键原理:
var无块级作用域,循环中只有一个 i 变量,异步回调执行时 i 已变为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. 代码题:无重复字符的最长子串
滑动窗口+哈希表解决