【前端校招面经】快手2023春招前端面经
base: bj
岗位: 前端
业务: 快手电商
背景: 这次是上一次电商一面后的二面
- 自我介绍
- 过往项目经历
- 讲讲你实现过的 React 组件
- 讲一讲你在过往实习经历里面, 最让你有成就感的事情是什么
- 如何衡量前端性能
- 前端页面如何排查 bug
衡量前端性能通常涉及以下几个方面:
-
加载性能:包括页面加载速度、资源加载时间等,可以使用浏览器开发者工具中的网络面板来查看各个资源的加载时间和顺序,以及页面整体的加载时间。
-
渲染性能:包括页面渲染速度、页面重绘和重排次数等,可以使用浏览器开发者工具中的性能面板来查看页面的渲染性能,并且可以使用一些性能分析工具来对页面进行性能优化。
-
交互性能:包括用户操作响应速度、动画流畅度等,可以通过浏览器开发者工具中的性能面板来查看页面的交互性能,以及使用一些动画库和性能优化技巧来提升页面的交互性能。
-
内存占用:包括页面内存占用情况、内存泄漏等,可以使用浏览器开发者工具中的内存面板来监控页面的内存占用情况,并且可以通过一些内存分析工具来定位和解决内存泄漏问题。
排查前端页面的bug通常可以按照以下步骤进行:
-
复现问题:首先需要确保能够稳定地复现出问题,包括复现的条件和步骤。
-
查看控制台报错:使用浏览器开发者工具中的控制台面板来查看页面的报错信息,包括JavaScript错误、警告等,以及网络请求的状态码和错误信息。
-
排查代码逻辑:根据报错信息和复现的步骤,逐步排查代码逻辑,查找可能导致问题的原因,可以通过添加日志、调试等方式来定位问题。
-
使用调试工具:使用浏览器开发者工具中的调试功能,例如断点调试、单步执行等,来进一步分析和排查问题。
-
查看网络请求:查看页面的网络请求情况,包括请求的参数、返回的数据等,以及请求的状态码和错误信息,排查可能存在的网络请求问题。
-
查看页面渲染:查看页面的渲染情况,包括HTML结构、CSS样式、JavaScript交互等,排查可能存在的渲染问题。
-
参考文档和社区:在排查问题的过程中可以参考官方文档、社区论坛、博客文章等资源,寻求帮助和解决方案。
- 事件循环: 看代码说结果
代码题: 获取一个数组中前 n 个最大的值, 你能想到几种方法
如果大数组的 length < n, 则递归处理小的子数组
获取数组中前 n 个最大值的几种方法:
- 排序法:先对数组进行排序,然后取前 n 个元素即可。时间复杂度为 O(nlogn)。
function getTopNMax(nums, n) {
return nums.sort((a, b) => b - a).slice(0, n);
}
- 堆排序法:利用最大堆数据结构,构建一个包含数组中前 n 个元素的最大堆,然后遍历剩余的元素,如果比堆顶元素大,则替换堆顶元素并重新调整堆,最后堆中的元素就是前 n 个最大值。时间复杂度为 O(nlogk),其中 k 为堆的大小。
function getTopNMax(nums, n) {
const heap = nums.slice(0, n);
buildMaxHeap(heap);
for (let i = n; i < nums.length; i++) {
if (nums[i] > heap[0]) {
heap[0] = nums[i];
maxHeapify(heap, 0);
}
}
return heap;
}
function buildMaxHeap(nums) {
for (let i = Math.floor(nums.length / 2) - 1; i >= 0; i--) {
maxHeapify(nums, i);
}
}
function maxHeapify(nums, i) {
const left = 2 * i + 1;
const right = 2 * i + 2;
let largest = i;
if (left < nums.length && nums[left] > nums[largest]) {
largest = left;
}
if (right < nums.length && nums[right] > nums[largest]) {
largest = right;
}
if (largest !== i) {
[nums[i], nums[largest]] = [nums[largest], nums[i]];
maxHeapify(nums, largest);
}
}
- 快速选择算法:类似于快速排序,通过快速划分数组,找到第 k 大的元素,然后返回前 k 个元素。时间复杂度为平均 O(n),最坏情况下为 O(n^2)。
function getTopNMax(nums, n) {
const kthLargest = quickSelect(nums, 0, nums.length - 1, nums.length - n);
return nums.slice(kthLargest);
}
function quickSelect(nums, left, right, k) {
const pivotIndex = partition(nums, left, right);
if (pivotIndex === k) {
return pivotIndex;
} else if (pivotIndex < k) {
return quickSelect(nums, pivotIndex + 1, right, k);
} else {
return quickSelect(nums, left, pivotIndex - 1, k);
}
}
function partition(nums, left, right) {
const pivotIndex = Math.floor(Math.random() * (right - left + 1)) + left;
const pivot = nums[pivotIndex];
let i = left;
let j = right;
while (i <= j) {
while (nums[i] < pivot) {
i++;
}
while (nums[j] > pivot) {
j--;
}
if (i <= j) {
[nums[i], nums[j]] = [nums[j], nums[i]];
i++;
j--;
}
}
return i - 1;
}
这些方法都可以获取数组中前 n 个最大值,具体选择哪种方法取决于实际情况和需求。
反问环节:
- 电商业务主要以 B端为主, C 端直接面向用户的很少
作者:四季奶青大杯正常糖
链接:www.nowcoder.com/discuss/481…
来源:牛客网
【前端校招面经】快手2023前端三面面经
base: bj/hz
岗位:前端开发
部门:电商
之前的一面面经在这里:www.nowcoder.com/discuss/480…
之前的二面面经在这里:www.nowcoder.com/discuss/481…
- 自我介绍
- 讲过去的实习项目经历
- 挨个问项目经历, 每一段项目经历都做了什么, 实现了哪些价值
- 讲每一段项目经历的背景, 需求和目标
- 用代码描述你实现过的页面和组件
- 你为什么考研, 为什么选择做前端
- 你平时是怎样自学前端知识的
- 前端代码规范
- Redux 和 Mobx 的区别
Redux 和 Mobx 都是用于管理应用状态的流行的状态管理库,它们在某些方面有所不同:
-
数据流方式:
- Redux 使用单一的不可变数据源和纯函数来处理状态变化。它通过 action 触发 reducer 来修改状态,然后通知所有订阅者(通过 connect 或 useSelector)状态已更新。
- Mobx 则采用了更加灵活的响应式编程模式。它允许你使用可观察的数据结构(observable)来存储状态,并在状态发生变化时自动通知相关的组件进行更新。
-
使用复杂度:
- Redux 相对来说更加显式和严格,需要定义 action 类型、编写 reducer、并使用 middleware 来处理副作用。
- Mobx 则更加简洁和自然,它的响应式机制能够自动处理状态的变化和相关的副作用,减少了编写和维护代码的工作量。
-
性能:
- Redux 由于使用了不可变数据和纯函数,可以更容易地实现时间旅行和记录状态的变化。但在大型应用中,可能会导致性能问题,因为每次状态变化都需要创建新的状态副本。
- Mobx 则可以通过观察者模式精确地跟踪状态的变化,并只更新受影响的组件。这种精确的响应性能够在某些情况下带来更好的性能表现。
-
学习曲线:
- Redux 的学习曲线相对较陡,特别是对于新手来说,需要理解一些概念如 action、reducer、middleware 等。
- Mobx 相对更容易上手,因为它更接近于传统的面向对象编程风格,而且不需要理解一些复杂的概念。
-
社区和生态系统:
- Redux 有一个庞大的生态系统,包括大量的中间件、开发工具和与其他库的整合。
- Mobx 的生态系统相对较小,但也有一些常用的库和工具可供选择。
总的来说,Redux 更适合大型应用或需要更严格的状态管理的场景,而 Mobx 则更适合中小型应用或对开发效率要求较高的场景。选择哪种状态管理库取决于具体的项目需求、团队技术栈和个人偏好。
- 回顾之前两轮面试的代码题
- 一面的代码题, 改成区分大小写的实现
- 一面的代码题, 优化一下一面的实现
- 二面的代码题, 给出优化实现
可以将前面的实现稍作修改,使其区分大小写。下面是改进后的代码:
// 一面的代码题, 改成区分大小写的实现
function getTopNMaxCaseSensitive(nums, n) {
const heap = nums.slice(0, n);
buildMaxHeap(heap);
for (let i = n; i < nums.length; i++) {
if (nums[i] > heap[0]) {
heap[0] = nums[i];
maxHeapify(heap, 0);
}
}
return heap;
}
function buildMaxHeap(nums) {
for (let i = Math.floor(nums.length / 2) - 1; i >= 0; i--) {
maxHeapify(nums, i);
}
}
function maxHeapify(nums, i) {
const left = 2 * i + 1;
const right = 2 * i + 2;
let largest = i;
if (left < nums.length && compare(nums[left], nums[largest]) > 0) {
largest = left;
}
if (right < nums.length && compare(nums[right], nums[largest]) > 0) {
largest = right;
}
if (largest !== i) {
[nums[i], nums[largest]] = [nums[largest], nums[i]];
maxHeapify(nums, largest);
}
}
function compare(a, b) {
if (a === b) return 0;
if (typeof a === 'string' && typeof b === 'string') {
return a.localeCompare(b); // 使用localeCompare进行字符串的大小写敏感比较
} else {
return a - b;
}
}
// 一面的代码题, 优化一下一面的实现(假设对输入数组进行预处理以保证数据正确性)
function getTopNMaxOptimized(nums, n) {
if (nums.length <= n) return nums.sort(compare).reverse(); // 数组长度小于等于 n 时直接返回排序后的数组
const heap = nums.slice(0, n);
buildMaxHeap(heap);
for (let i = n; i < nums.length; i++) {
if (compare(nums[i], heap[0]) > 0) {
heap[0] = nums[i];
maxHeapify(heap, 0);
}
}
return heap.sort(compare).reverse(); // 返回前 n 个最大值并保持原始顺序
}
// 二面的代码题, 给出优化实现(假设输入数组已经按需求进行了预处理)
function getTopNMaxFinal(nums, n) {
if (nums.length <= n) return nums.sort(compare).reverse(); // 数组长度小于等于 n 时直接返回排序后的数组
const result = [];
for (let i = 0; i < n; i++) {
let maxIndex = i;
for (let j = i + 1; j < nums.length; j++) {
if (compare(nums[j], nums[maxIndex]) > 0) {
maxIndex = j;
}
}
result.push(nums[maxIndex]);
[nums[i], nums[maxIndex]] = [nums[maxIndex], nums[i]]; // 将找到的最大值移到前面
}
return result;
}
这些实现都能够在区分大小写的情况下获取数组中前 n 个最大值,并且进行了一定程度的优化以提高效率。
- 反问环节
- 快手电商业务背景
- 竞争对手
- 主要赛道和下沉市场
- 和拼多多, 淘宝, 抖音等竞争对手相比的优势
作者:四季奶青大杯正常糖
链接:www.nowcoder.com/
来源:牛客网