英飞流全栈开发实习面试:React 原理与 JS 基础连问
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 请做一下自我介绍。
- 介绍一下你的实习经历。
- 介绍一下你的项目。
- 介绍一下 ES6 有哪些新特性。
- 讲解一下 React 的版本区别。
- 介绍一下 React 的事件机制。
- 介绍一下 React 的 diff 算法。
- 讲解一下 Vite 和 Webpack 的区别。
- 介绍一下 CommonJS 和 ESM 的区别。
- 讲解一下闭包是什么。
- 讲解一下 JS 里的继承。
- 你对后端了解多少?
- 介绍一下 Redis。
《参考解析》
React 的事件机制
React 不把监听逐个绑到节点上,而是统一委托到根容器,用合成事件抹平浏览器差异。事件触发时,React 从触发节点沿 Fiber 树向上收集所有绑定,构造事件队列并按捕获、冒泡阶段依次执行。两个高频变化点要记住:React 17 起委托挂载点从 document 改成 React 根容器,同时也去掉了事件池(不再需要 e.persist())。因为走合成事件,和原生 addEventListener 混用时要注意顺序——挂在根容器之上的原生监听往往先执行,stopPropagation 也可能拦不住外层原生监听;需要彻底阻断时用 e.nativeEvent.stopImmediatePropagation() 之类的原生手段。
diff 算法与 Fiber
React 的 diff 建立在两条启发式上:类型不同的元素直接整棵替换,同层元素只做同层比较(跨层移动不复用)。列表 diff 靠 key 匹配旧节点,从而复用 DOM、只做移动;用下标当 key,在头部插入或删除时会错位复用,导致内部状态串到别的条目上,这是最常见的坑。Fiber 则是把渲染工作拆成可中断的单元,用链表维护父子与兄弟关系,渲染阶段可被打断、提交阶段不可打断——这正是时间切片与 useTransition 能成立的前提。回答时把「key 的作用」和「为什么要 Fiber」两件事讲清楚,比复述算法步骤更得分。
React 各版本差异
16 引入 Fiber 重写与错误边界,16.8 带来 Hooks;17 是过渡版本,主要动作是事件委托挂到根容器、移除事件池、新的 JSX 转换(不再需要显式 import React),目标是让渐进升级更平滑;18 让并发渲染落地——createRoot、自动批处理、useTransition / useDeferredValue、Suspense 的流式 SSR 与选择性注水;19 继续往服务端与客户端一体化走,Actions 与 useActionState 处理表单与异步状态,ref 可直接作为 prop,不再需要 forwardRef。答这题的关键是说出每个版本的标志性动作,而不是罗列 API;被追问「并发渲染到底并发了什么」时,答「把渲染切成可中断单元、让高优先级更新插队」即可。
Vite 与 Webpack、CommonJS 与 ESM
Vite 开发态不做整体打包,靠浏览器原生 ESM 按需加载,只对依赖做一次 esbuild 预构建,冷启动与 HMR 与项目规模基本无关;Webpack 必须先构建完整依赖图,大项目明显更慢。生产构建 Vite 交给 Rollup,Webpack 生态的 loader/plugin 更全,老项目迁移成本更低。CJS 与 ESM 的差别在加载时机与绑定语义:CJS 运行时加载,require 可以写在条件分支里,导出的是值的快照,模块对象可改写;ESM 是编译期静态分析,import / export 必须在顶层,导出的是活绑定,模块内变量变化会反映到导入方,因此打包器能做 tree-shaking,也支持顶层 await 与动态 import()。循环依赖上 CJS 可能拿到半成品 exports,ESM 因为有提升会先建立绑定但访问未初始化值会报错;ESM 里没有 require、没有 __dirname,要用 createRequire 和 import.meta.url 转换。
闭包
闭包是函数与其定义时词法环境的组合:内层函数引用了外层作用域的变量,只要这个函数仍被引用,那些变量就不会被回收。它的用途是数据私有与状态保持——防抖节流里的 timer、模块模式、以及 React 每次渲染形成的作用域快照,本质都靠闭包。要能说出两类坑:循环里用 var 会让所有回调共享同一个变量(用 let 形成每轮独立绑定解决),以及被长期持有的闭包导致大对象无法释放(事件监听忘了解绑是典型)。被追问「闭包存在哪」时答「变量在堆上的环境记录里,由函数对象的内部槽引用」,比说「存在栈上」准确。
JS 的继承
主线是原型链:每个对象有内部原型指向构造函数的 prototype,属性查找沿链向上直到 null。class 只是语法糖,extends 与寄生组合继承的关键差别是——class 继承会先创建子类 this 再调用父类构造(super),并且原型链两级都正确连好;而手写继承容易只连 Child.prototype 忘记修正 constructor 指向。实际工程里深继承层次很少见,更推荐组合、mixin 或工具函数;面试里常被追问的是 instanceof 的判定原理、new 做了什么(创建对象、连原型、执行构造、返回对象),以及 Object.create 与 Object.setPrototypeOf 的区别。
对后端的了解与 Redis
实习岗问后端通常只到「知道多少」这一层,但要答得具体。Redis 快的三个原因:数据在内存、单线程事件循环避免锁竞争与上下文切换、高效的数据结构与 IO 多路复用(6.0 起网络 IO 用多线程,命令执行仍是单线程)。按场景报数据结构:String 做缓存与计数器(INCR)、Hash 存对象、List 做队列、Set 做去重、ZSet 做排行榜与延时队列。缓存三件套必须能答:穿透用布隆过滤器或空值缓存、击穿用互斥锁或逻辑过期、雪崩靠过期时间加随机与多级缓存。持久化讲清 RDB 与 AOF 的取舍(前者快但可能丢数据,后者更安全但恢复慢,生产常用混合持久化),再补一句大 key、热 key 的排查,这一题就完整了。