背景
作为一名7年多老前端,最近有两个以前的老同事在京东给我发了份邀请,然后我了解了下,4天时间潦潦草草刷了下八股,开始4年后第一次社会面试(6年前到字节三面过了,嗯手写vue模版引擎和直播间uid统计,四年前二刷字节一面就挂了,只问了项目对方可能发现不对口)。这次面我的是一个T8,按照T7的标准来面试。结果是第一面就没过,准备的八股和项目是一个没问,问其他八股去了。接下来我将按照内容和复盘两个方向去记录这次不可直视的面试...
面试前这4天我都准备了些啥
- css基础
- 盒子模型
- 各类型选择器和优先级
- BFC
- flex和grid
- 三栏布局的实现,传统(圣杯布局,双飞翼布局),现代的(flex实现,grid实现)
- 动画和轮播的实现
- 浏览器渲染过程,回流/重排和重绘如何优化
- js基础
- 基础数据类型
- 数组
- 常见的方法
- v8下数组的实现,快慢数组和内存分配规则
- 原生的sort排序原理
- 常见的几种排序方法,双重循环实现(冒泡,选择,插入),分而治之(归并,桶排,快排)
- 内存管理
- 变量存储在堆和栈哪里
- js是怎么解析成机器码的
- var、let、const和变量的声明提升
- 垃圾回收
- 标记清除和引用计数清除
- v8优化,新老生代和增量回收
- 作用域和this
- 面对对象
- 定义和oop六大原则
- 原型和原型链
- 六种方式实现继承和各个的优缺点。原型链、构造函数、组合、原型式、寄生式,寄生组合式
- new 一个构造函数发生了什么
- dom事件处理。捕获=》触发=》冒泡
- Event Loop,node和浏览器下的事件循环机制包括宏、微任务
- promise
- 手写实现
- 并发数控制
- 如何中断
- ajax
- XMLHttpRequest
- axios
- 跨域处理
- XSS和CSRF防御
- http
- 协议构成
- 浏览器请求报文分析协议
- 常见的状态码 2xx,3xx等
- 网络七层模型和简化的四层模型
- 三次握手和四次挥手
- http1.0 http1.1 http2 http3的区别
- ssl/tsl加密过程,其实就是非对称加密传输对称加密的密钥
- 缓存
- 强缓存 cache-control 和 expires
- 协商缓存 Last-Modified/If-Modified-Since 和 Etag/If-None-Match
- 提升缓存的手段,cdn和webpack持久化构建
- 闭包和柯里化
- 闭包怎么来的
- 柯里化是啥,经典题目add(1,2,3) === add(1)(2)(3)还有接口域名等参数柯里化处理
- 动画
- 帧率计算
- requestAnimationFrame
- requestIdleCallback
- setTimeout
- 节流和防抖实现
- 框架
- vue
- 设计理念
- 双向绑定的源码实现和object.defineProperty与proxy区别
- html模版解析生成执行函数H的实现
- 基于微任务的渲染调度实现
- nextTick和$forceUpdate
- 3和2的diff算法区别
- 和react的区别
- react
- 设计理念
- setState 异步还是同步?
- 虚拟dom的diff过程
- useState和useMemo的源码大致实现
- fiber的演进和时间分片实现
- 为啥调度要用MessageChannel宏任务而不是微任务promise实现
- babel 常见的全家桶都是干啥用的,源码转换怎么用
- h5
- 屏幕适配,vw,rem,em,px
- ios下的点击穿透
- 输入框键盘弹起事件
- bridge
- vue
- 项目
- 流量广告后台下的各种技术难点和解决方式
- BI平台难点和演进
- 大数据量渲染
- 多引擎数据适配
- 数据大屏设计和难点
- 自实现透视表算法的改进
- 报表的秒渲染优化过程
- 指纹浏览器的难点和设计
- electron
- 如何一机打三包(windows,mac inter, mac m系列)
- 如何无签名实现自动更新
- 如何保证代码安全
- h5低代码如何设计
- webpack后台脚手架
- rbac权限
- 项目模版的目录划分
- 持久化构建。增量构建和提升浏览器缓存命中率
- vite6 h5活动脚手架
- 增量构建和发布
- 通用代码的写单测过程中的难点
- 自定义实现各种plugins,html,cdn引入,预渲染,px转vw和rem共存,asset统计,动态代理和mock
- 通用工具库的实现
- 算法
- 链表
- 队列和优先级队列
- 树
- 旅行商问题
面试过程和相应的复盘
自我介绍
面试时晚上,白天还想着临时抱佛脚,又刷了一整天,晚上脑袋晕乎乎的,自我介绍只能临场发挥。
内容
讲的顺序就是
- 学历背景
- 公司经历,哪一年入职了哪一家公司,在里面做了啥,取得哪一些成绩(很粗糙)
- 个人的喜好和性格方面:主要表达意思就是我很阳光的,很能跟进时代的,很能融入团队的,嗯
复盘
简历能放的东西太少了,在描述公司和项目经历时候应该多说点体现个人能力的事情,在描述方向上应该:
- 项目如何的复杂(技术过关)
- 独立负责(抗压)
- 主动推动项目向前(owner意识)
- 如何同部门跨部门借力(人力有时穷) 描述这些事情的基础就是,你能回答出来的方向上去靠,因为面试官可能会根据你的介绍去问相应的基础,接下里有的题目就问到了
题一,简单做一道题看看你的写代码思路和习惯。小张要对小梅表白,实现一个函数makeConfession(isOk), 其中isOk是预设值,如果传入是true, 在promise内20ms-50ms后resolve('ok'),防之reject'no'
我当时的反应和作答
看完题目,我愣了,你都写了这么清楚了我还怎么体现我的写代码思路...
function makeConfession(isOk: boolean) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (isOk) {
resolve('ok')
} else {
reject(new Error('no'))
}
}, Math.ceil(Math.random() * 20 + 30))
}).catch((err: any) => {
// 为啥是any不是Error,因为面试官让我说,他写,然后他那边的编辑器过不去,时候我自己在自己的vscode发现可以的
return err.message
})
}
// 测试函数,用于演示makeConfession方法在不同参数下的调用结果
async function test() {
const res1 = await makeConfession(true)
const res2 = await makeConfession(false)
console.log('res1', res1) //
console.log('res2', res2)
}
test()
面试官看我后不让我.catch()
好,那我就try catch吧
function makeConfession(isOk: boolean) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (isOk) {
resolve('ok')
} else {
reject(new Error('no'))
}
}, Math.ceil(Math.random() * 20 + 30))
})
}
// 测试函数,用于演示makeConfession方法在不同参数下的调用结果
async function test() {
const res1 = await makeConfession(true)
if (res1 === 'ok') {
console.log('表白成功测试完成')
}
try {
await makeConfession(false)
} catch (err: any) {
if (err.message === 'no') {
console.log('表白失败测试完成')
}
}
}
test()
然后面试官问我怎么去执行它,不能在vite项目下的入口文件import引入
我想了下,直接在终端tsc 这个文件然后在node 编译后的文件,然后面试官机器化执行我说的操作,发现终端没有这个命令,(我当时心里想,没有你倒是全局装啊, 电脑是你的,你还装不了?)嗯我看了是vite项目,说下啦在package.json中的脚本加入这个命令,嗯跑起来了,结果也对了
然后面试官问我,为啥这个tsc命令在终端跑不来,到这个package.json中却跑起来了
忘了,不知道
现在上面代码有两个回调,能不能优化下,让它变得易维护可读
这里我没想明白他想干啥,你是想让我把settime抽离成delay呢还是想我第一个if直接return不要else呢,平时项目的习惯是代码逻辑复杂了才会去考虑优化和增强可读性,对于写了这么多项目代码的我来说,这就是可读性最强的了,初学者能看的懂的对我来说就是可读性和维护性比较强的了,毕竟项目接手项目的人不都是牛人,可能是混子,牛逼框架源码里的那一套对他们来说没用。然后第一题就过去了
复盘
事后我想了想,习惯性让我没意识到这是面试场景,而不是平时,既然要八股,那就必须按照四书五经来,简单的改改吧
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
async function makeConfession(isOk: boolean, ms: number) {
await delay(ms)
return new Promise((resolve, reject) => {
if (isOk) {
return resolve('ok')
}
reject(new Error('no'))
})
}
题二,我们来考考基础知识吧,面试官打开了他项目下的package.json,一个个的问了我这些都是干啥的,pnpm-lock文件中的版本号用来做啥的
答不出来的有 private,type中如何同时能用两种规范es module和commonjs, dependencies和devdependencies的区别,tsc命令为何能在package.json脚本中执行,然后喷了我下,你平时开发脚手架和库都不了解这些属性的么?
复盘
看了我前面的准备目录,我还真没准备复习package.json,包括tsConfig.json和一些构建工具的字段作用
- 比如
private事后我才想起这玩意是不希望发布到公共 npm 仓库上,可以将 private 设为 true。坐脚手架和工具库都是几年前的事情了,现在这些细节肯定想不起来,哎还是得重刷。 type中如何同时能用两种规范es:type是module情况下,将commonjs规范的文件改为.cjs,就可以在es moudle中import导入了dependencies和devdependencies的区别:运行时依赖和开发依赖,错误使用的影响:- 若将构建工具(如 vite)误放入 dependencies,会导致用户安装无用包,增加体积。
- 若将运行时库(如 lodash)误放入 devDependencies,项目上线后会报错。
tsc命令为何能在package.json脚本中执行:我们在安装包的时候,会在 node_modules/typescript/bin/tsc 拷贝到 node_modules 下的.bin中,所以我们在执行 npm run test 的时候,会去 node_modules/.bin 下寻找 tsc 的命令执行文件,所以不需要全局安装 tsc 也能执行。
题三 你说你优化了fcp,fcp是啥,为啥你只优化它,其他的比如FID为啥不优化,fcp的定义是啥,lcp的定义是啥
我当时的回答
- fcp: 首次内容渲染
- 为啥只优化它,因为h5活动,基本没啥表单,在直播间以用户能够马上看到页面为主,FID的优化没啥意义
- fcp定义,不知道,想不起来了
- lcp: 只知道是最大内容绘制耗时,定义不知道
题四 简单的说说vue和react的区别
这东西说多能说很多,时间不够,我就说了下设计理念,这不面试官让我简单说说
- vue MVVM 数据双向绑定 开发者体验友好
- react 单向数据流,函数式编程,自由度高,开发者上手难度高一些
复盘
还是得结构化下表达,多一点时间无所谓,让面试官等着
| 对比项 | Vue(MVVM) | React(单向数据流 + 函数式) |
|---|---|---|
| 编程模式 | MVVM,结构清晰,双向绑定 | 非传统 MVC,更自由,函数式和组件式 |
| 数据绑定 | ✅ 双向绑定(v-model) | ❌ 无双向绑定,用 props 和 setState或useState |
| 模板语法 | HTML 模板 + 指令(如 v-if, v-for) | JSX,逻辑和结构合并,灵活可组合 |
| View | 与数据关系 ViewModel 自动监听响应式数据 | 明确通过状态更新触发视图重渲染 |
| 学习曲线 | 更接近 HTML,初学者易上手 | 函数式思想强,更灵活但入门有难度 |
| 响应式系统 | Vue 内建响应式系统(基于 Proxy) | 使用 useState、useEffect 手动管理状态 |
复盘
简历里写了好几个性能优化,却没有好好回忆下这些知识点
- fcp定义:首次绘制任何文本、图像、非空白canvas或者SVG的时间点。chrome要求1.8s内
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntriesByName('first-paint')) {
console.log('fp', entry);
}
}).observe({ type: 'paint', buffered: true });
- lcp定义:在用户首次交互前,当前可视区域内统计的面积最大的那个元素的重页面加载到渲染中耗时,chrome要求2.5s内
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('lcp', entry);
}
}).observe({ type: 'largest-contentful-paint', buffered: true });
lcp与fcp时间越接近,阻塞时间越短
题五 面试官不想面我了,哈哈哈,旁边冒出来一个人,问了下我,如果你和服务端发生了冲突,你该怎么解决,比如api接口字段的封装,前端希望能直接拿来用,后端觉得自己还要二次处理,麻烦增加他的开发量
其实到了这里,我就知道凉凉了,直接回到,以大局的角度来说服后端,比如产品的用户体验等,然后对方问了下我之前的职级,面试就结束了
复盘
事后想了想,这种职场矛盾问题应该用一些所谓的沟通法则去回答会比较好,装逼的词语不可少,比如这里可以这么说,使用黄金沟通四大法则:
- 先说对方想听的
- 再说对方听得进的
- 然后再说你该说的
- 最后再说你想说的
总体复盘
项目是一个没问,框架源码没问,算法没问,三种感受
- 八股还是刷少了啊,特别简历提到的一定要刷完
- 面试当天不能刷题,就和高考一样,保持清醒的大脑比较重要
- 自我介绍很重要,不能临时发挥,要把思路写下来去引导面试官接下来可能要问你的地方