ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

百度2020校招前端笔试题全解析:从JS基础到框架原理

百度2020校招前端笔试题全解析:从JS基础到框架原理 1. 试卷整体观察一场典型的“大厂海选”式考察拿到这份百度2020校招Web前端工程师笔试卷第一批第一反应是熟悉。如果你经历过那个年代的校招季应该对这种卷子有印象——它不像社招那样深挖某个方向的细节而是更像一张“能力扫描仪”在有限的两个小时里尽可能把候选人的知识面、基本功、临场写代码的能力都过一遍。先说结论这份试卷的核心命题思路可以概括为三句话——基础不牢地动山摇框架再熟原理要懂算法不练笔试白干。从题型编排来看2020年这个时间节点非常典型。那会儿 Vue 2 正处于巅峰期Vue 3 还在 beta 阶段React 16.8 刚推出 Hooks 不到一年前端工程化已经全面普及但还没有像现在这样人手一个 monorepo、动不动就上 WASM。所以试卷里的题目很好的反映了那个时期的行业状态ES6 已经全面普及框架原理开始成为必考点HTTP 和浏览器缓存依然是大头手写代码题从“能不能写出来”变成了“写得好不好、边界处理全不全”。我在帮很多人复盘这种试卷时发现一个规律凡是能把下面这四块准备扎实的人基本都能过笔试关——JavaScript 语言本身尤其是原型链、闭包、异步这套东西浏览器工作原理和网络基础缓存、渲染机制、HTTP 协议框架的实际使用但更重要的是设计思想手写代码能力和基础算法思维不要求你 ACM 金牌但基本的数据结构和常见题的套路你得有。这篇文章就按这个逻辑把这份试卷的考点逐层拆开每一块我都给出当年的题目回忆、正确的解题思路以及我踩过/见过别人踩过的坑。不管你是正在准备校招的应届生还是工作几年想回头补基础的同行这份拆解应该都能帮你把“学过的知识”串成“能答题的能力”。提示所有题目均为基于该批次考试常见考点的回顾与重构并非原始试卷的逐字复刻但考点覆盖和出题思路完全对齐那一年的真实风格。2. 基础能力篇JS 语言特性与浏览器原理绕不开的“内功”2.1 原型与原型链面试官最爱问、最见功底的一题几乎每份前端笔试卷都有原型链的题百度这份也不例外。原题的大意是考察对prototype、__proto__和Function/Object之间关系的理解要求写出下面这段代码的输出function Person(name) { this.name name; } Person.prototype.sayHello function() { console.log(Hello, this.name); }; const p new Person(Alice); console.log(p.__proto__ Person.prototype); console.log(Person.prototype.constructor Person); console.log(p instanceof Person); console.log(Person.__proto__ Function.prototype); console.log(Object.prototype.__proto__);答案是true、true、true、true、null。这道题看似简单却能刷掉不少人。很多人能答出前四个但第五个容易卡住——Object.prototype的原型是null因为它是原型链的终点。我见过不少候选人卡在这一步原因不是不知道而是平时写业务代码时根本没机会接触这些底层关系。我的建议是必须亲手画一遍完整的原型链图。不要只看文章拿张纸把Person、Function、Object之间的关系画出来标注每条边的方向。画完之后你自然能理解为什么Function.__proto__ Function.prototype也成立——这是新手最容易纠结的点Function 是一个函数同时它又是通过自身的 prototype 构造出来的形成了一个看似自指的结构。这类题拿分的重点是理解三个规则new构造的对象以构造函数的prototype为原型实例通过__proto__寻找原型链上的属性和方法所有函数的__proto__都指向Function.prototype而它本身也是一个函数所以Function.__proto__又指向它自己。2.2 作用域与闭包从一道改错题聊起笔试卷中闭包的考察非常多样。我记得那份卷子里有一道这样的改错题现在看依然很经典。题目给出了一个循环注册事件的代码要求修正让它输出正确的索引for (var i 0; i 5; i) { setTimeout(function() { console.log(i); }, 1000); }如果直接跑输出的是 5 个5。原因是setTimeout的回调函数访问的是同一个变量i当回调真正执行时循环早已结束i已经变成 5。修正方案有三个层面从传统到现代第一个方案是 ES5 的经典做法用立即执行函数创建独立作用域for (var i 0; i 5; i) { (function(j) { setTimeout(function() { console.log(j); }, 1000); })(i); }第二个方案是把var改成let因为let在for循环的每个迭代中都会重新绑定变量for (let i 0; i 5; i) { setTimeout(function() { console.log(i); }, 1000); }第三个方案是用bind传参for (var i 0; i 5; i) { setTimeout(console.log.bind(console, i), 1000); }这道题真正的价值不在“怎么改”而在“为什么这样改就能好”。当年我在准备面试时用红笔在书边写了一句现在看来很朴素的话只要回调函数访问的变量是在父级作用域声明的而这个回调整体被延迟执行就必须警惕变量捕获的时机问题。let之所以能解决是因为它每次迭代都会创建新的词法环境IIFE 之所以能解决是因为它通过函数参数把值“拍了个快照”传给内部作用域。2.3 浏览器缓存机制网络题里的“送分题”与“送命题”二面之前的那场笔试网络部分的题量一直不小。百度这份卷子考了缓存题目大意是页面加载一个app.js第一次请求返回 200后续刷新时返回 304请问服务端怎样设置响应头才能让浏览器在强缓存生效期间不发起网络请求这里很多人会混淆强缓存和协商缓存我直接给标准答案和背后的逻辑。强缓存有Expires和Cache-Control两个头。Expires是 HTTP/1.0 的老方案值是绝对时间缺点是客户端时间不准就会失效。Cache-Control是 HTTP/1.1 的方案常用值是max-age3600表示相对时间3600 秒内直接读本地缓存不发任何请求。协商缓存靠的是Last-Modified/If-Modified-Since和ETag/If-None-Match。第一次请求时服务端返回ETag资源内容的哈希标识浏览器缓存下来下一次请求时浏览器带上If-None-Match头服务端对比当前资源的 ETag如果没变则返回 304浏览器继续用本地缓存如果变了则返回 200 和新的资源。回到题目本身如果希望资源在生命期内完全不发请求应该设置Cache-Control: max-age86400这种强缓存如果希望每次重新验证则设置Cache-Control: no-cache注意no-cache的意思是“使用前必须验证”而不是“不缓存”配合ETag使用。还有一个容易混淆的no-store那才是真正的不允许任何缓存。当年我见过很多答案把no-cache和no-store搞混也有人不知道 304 本质上依然是“一次网络请求”只是响应体很小。这些都属于原理没吃透的表现。笔试不是让你背字段而是在考察你能不能解释清楚一次完整的请求-缓存-命中流程。前端开发中缓存策略的坑特别多。我个人的经验是指纹化的静态资源比如app-8d3c2f.js用强缓存让浏览器直接本地读HTML 文件用no-cache配合协商缓存保证页面更新后能快速拿到最新的资源引用列表如果上线后发现问题需要紧急修复就改版本号或 hash。说到底缓存策略的核心是一个“平衡”在性能和及时性之间找那个最适合业务场景的落点。3. 框架原理篇会调用只是入门懂设计才算合格3.1 Vue 响应式原理经典的 Object.defineProperty 时代2020 年的 Vue 题基本围绕 2.x 展开核心考点集中在一个地方响应式系统是如何工作的。原题大概要求先解释v-model的原理再手写一个简单的响应式实现。v-model的本质是语法糖。在 Vue 2 中它等价于:value加input。对input元素来说v-model会绑定 value 属性同时监听 input 事件事件触发时把输入值写回数据。对自定义组件来说v-model默认监听的是value属性和input事件但你也可以通过model选项修改这两个默认名称。更深层的响应式原理分三块Observer遍历数据对象的属性用Object.defineProperty把每个属性都改成 getter/setter。当属性被访问时get收集当前的依赖Watcher当属性被修改时set通知所有依赖进行更新。Dep依赖管理器每个响应式属性都有一个 Dep 实例负责收集和通知 Watcher。Watcher观察者。组件渲染时生成一个 Watcher当它访问了某个响应式属性后就被收集进该属性的 Dep 里。一道经典的面试追问是Vue 2 为什么无法检测到数组和对象的新增属性因为Object.defineProperty是在初始化时对已有属性做的拦截新增属性并没有经过这个转换所以不是响应式的。Vue 2 为此提供了Vue.set方法它会手动调用observe为新属性建立响应式并触发更新。数组方面Vue 2 直接替换了push、pop、shift、unshift、splice、sort、reverse这七个方法在调用后手动触发更新所以通过下标赋值arr[2] x也是无法触发更新的。可以现场写一个简易版响应式系统帮助理解核心机制function observe(obj) { if (typeof obj ! object || obj null) return; Object.keys(obj).forEach(key { let value obj[key]; observe(value); const dep new Dep(); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { if (Dep.target) dep.add(Dep.target); return value; }, set(newValue) { if (newValue ! value) { value newValue; observe(newValue); dep.notify(); } } }); }); } class Dep { constructor() { this.subs []; } add(sub) { this.subs.push(sub); } notify() { this.subs.forEach(sub sub.update()); } } Dep.target null;这段代码是 Vue 2 响应式系统的骨架笔试时能默写出来基本就能证明你是真的理解了原理而不是背了两篇面经就上考场。3.2 React Hooks 与设计理念踩在新旧交替的节点上2020 年 React 16.8 的 Hooks 已经发布一年多笔试卷里出现相关题目很正常。我记得有题是让解释useState和useEffect的原理并要求从类组件思维切换到 Hooks 思维。这道题考查的点很实际Hook 的调用顺序为什么不能变这背后是 Hooks 的核心实现机制——React在组件渲染时按调用顺序把每个 Hook 的状态存在一个链表里。如果条件渲染导致某个 Hook 在某次渲染中没有被调用后续所有 Hook 拿到的状态就会错位。所以 Hook 必须在组件顶层调用不能写在条件语句里。useEffect的题也很容易出变种让你比较不同的依赖数组写法。最常见的一个坑依赖数组传空数组[]和完全不传依赖数组效果完全不同。空数组表示只执行一次mount 时执行unmount 时清理不传表示每次渲染都执行传入[a, b]表示 a 或 b 变化时执行。笔试时可以用一个对比表格来回答清晰又高效写法执行时机适用场景不传依赖每次渲染后执行不推荐容易死循环[]仅 mount 时执行初始化请求、一次性订阅[a, b]a 或 b 变化时执行依赖特定状态的副作用我见过一些候选人讲了 Hooks 的用法却说不清为什么比类组件好。这里有一个很本质的差异类组件的生命周期方法是“按时间点”组织的而 Hooks 是“按功能”组织的——把相关的逻辑放在同一个 Effect 里而不是分散在componentDidMount、componentDidUpdate、componentWillUnmount三处。这也是 Hooks 设计上最重要的价值。3.3 框架题答题技巧原理题要有层次在校招笔试里框架原理题的作答是可以有套路的不是背标准答案就行。我的经验是不管遇到什么原理题都按“用法 → 实现机制 → 设计价值”三层结构来写。第一层交代基本用法让阅卷人确认你用过这个功能第二层深入源码机制比如 Vue 的依赖收集或 React 的 Fiber 调度证明你不只是 API 调用者第三层谈设计价值为什么 Vue 2 用Object.defineProperty而不是 Proxy那会儿 Proxy 的兼容性和性能还不理想为什么 React 要引入 Fiber因为旧版递归调用的 stack reconciler 无法中断长任务会阻塞渲染线程。这种答题结构基本能把一道原理题变成你的加分项。我特意提醒一句笔试回答原理题时不要只写概念一定要给出关键代码片段。哪怕不完整哪怕是伪代码也能向面试官传递“我确实写过、深入过”的信号。空对空的概念陈述在笔试中得分率非常低。4. 手写代码篇从“会做”到“做得对”差的是一堆边界条件4.1 防抖和节流手写频率控制函数的标准答案百度这轮笔试考了一道手写防抖debounce和节流throttle这是前端手写题里的常青树。题目要求实现一个通用的防抖函数并说明防抖和节流的区别。先给出标准实现// 防抖触发后等待 delay 毫秒如果期间再次触发则重新计时 function debounce(fn, delay) { let timer null; return function(...args) { if (timer) clearTimeout(timer); timer setTimeout(() { fn.apply(this, args); timer null; }, delay); }; } // 节流每隔 interval 毫秒最多执行一次 function throttle(fn, interval) { let last 0; return function(...args) { const now Date.now(); if (now - last interval) { last now; fn.apply(this, args); } }; }防抖和节流的区别可以用一个生活中的例子讲明白。防抖就像电梯关门——如果不断有人进来电梯就不断重新等只有停止进人之后才开始关门。节流就像游戏人物的技能冷却——技能用完之后必须等冷却时间结束才能再用不管你怎么按。笔试时虽然只要求写出实现但我会建议多写两行注释把适用场景标清楚防抖适合输入框搜索用户停止输入后才发请求、窗口 resize 后的计算节流适合滚动事件监听、按钮点击防重复提交。这道题的高分关键在边界处理上第一返回的函数要保留this指向所以用fn.apply(this, args)第二需要考虑是否要立即执行一次——有的业务场景要求第一次点击立即生效后续点击防抖这也是面试官喜欢追问的变种。4.2 深拷贝递归容易完美的边界处理很难手写深拷贝也是那张卷子的重点题目现在仍然是前端面试的高频考题。题目要求手写一个深拷贝函数能够拷贝对象、数组、函数、日期对象等基本类型。一个符合面试要求的深拷贝实现如下function deepClone(source, map new WeakMap()) { if (source null || typeof source ! object) return source; if (source instanceof Date) return new Date(source); if (source instanceof RegExp) return new RegExp(source.source, source.flags); if (map.has(source)) return map.get(source); const target new source.constructor(); map.set(source, target); Object.keys(source).forEach(key { target[key] deepClone(source[key], map); }); return target; }为什么要用 WeakMap因为深拷贝会遇到循环引用的问题。比如对象a.self a如果不处理递归会无限循环直到栈溢出。WeakMap 可以记录已经拷贝过的对象再遇到时直接返回拷贝副本从而打破循环。还要注意 Date 和 RegExp 需要特殊处理因为它们不是普通对象直接遍历属性会丢东西。函数在这份代码里会被原样返回——函数在深拷贝场景下一般复用引用即可没必要递归克隆这是行业里的通行做法。我见过很多候选人能写出递归遍历对象的版本但很少意识到要处理循环引用。这就是“会写”和“写对”的区别。准备手写题时千万别满足于“能跑”要多问自己几个问题如果是数组怎么办日期对象怎么办两个对象互相引用怎么办把这些边界情况都兜住了才是笔试拿满分的水平。4.3 手写 Promise.all异步题里的“必刷题”手写Promise.all是我建议每个求职者必须滚瓜烂熟的题。百度这份卷子也考了类似方向的异步题要求实现一个Promise.all或Promise.race的简易版本。一个符合规范的Promise.all实现如下function promiseAll(promises) { return new Promise((resolve, reject) { const results []; let count 0; for (let i 0; i promises.length; i) { Promise.resolve(promises[i]).then(value { results[i] value; count; if (count promises.length) resolve(results); }).catch(reject); } }); }这里有两个关键点。第一Promise.all的返回值顺序必须保持和输入顺序一致所以要用results[i] value而不是push。第二Promise.all的机制是“只要有一个 reject整个 Promise 立即 reject”所以.catch(reject)直接透传错误。如果用Promise.all处理并发请求有一个场景需要特别注意假设你有 10 个请求其中一个必挂但你希望其他 9 个的结果仍然能返回。这时不能直接加 catch而要在每个 promise 上加 catch保证返回的始终是 resolvedconst safePromises promises.map(p p.catch(err null)); const results await Promise.all(safePromises);手写这道题的价值不仅在笔试本身工作中尤其是做并发控制和批量请求时其实经常要自己组合类似逻辑理解底层实现细节会非常有帮助。4.4 JS 实现事件总线藏在业务代码里的“暗题”很多时候笔试卷不会直接考 EventEmitter但会以“实现一个简单的发布订阅模式”的方式出现。百度那轮笔试中有一道类似的题目要求实现一个事件总线支持on、off、emit三个方法。参考实现class EventBus { constructor() { this.events {}; } on(eventName, fn) { if (!this.events[eventName]) { this.events[eventName] []; } this.events[eventName].push(fn); } off(eventName, fn) { if (!this.events[eventName]) return; if (!fn) { delete this.events[eventName]; return; } this.events[eventName] this.events[eventName].filter(item item ! fn); } emit(eventName, ...args) { const fns this.events[eventName]; if (fns fns.length) { fns.forEach(fn fn(...args)); } } }发布订阅模式在业务代码中的典型应用是组件间通信特别是兄弟组件、多个模块监听同一个数据变更、全局状态变化通知。这类题手写难度不大但要注意off的边界情况不传fn时表示清空该事件的所有订阅传了fn时只移除指定的那个回调。还能再延伸一步加一个once方法注册时包一层函数触发后自动解绑这也是 EventEmitter 里非常常见的能力。手写题部分是我的建议是考前把“防抖、节流、深拷贝、Promise.all/race、发布订阅、数组去重、数组扁平化、柯里化”这八道题全部写成肌肉记忆闭眼能默写。2020 年的题库和今天相比差异不大这些题的出镜率实在太高了值得背到如同条件反射的程度。5. 算法与逻辑题不拼竞赛金牌但拼基础思维5.1 数组去重与字符串处理送分题也可以拿满分百度那场笔试的编程题部分没有出特别难的算法题整体难度对非 ACM 方向的同学比较友好。但“友好”不代表可以轻视反而说明这类题的区分度完全在于细节和边界。数组去重这道题从大一学 JS 起就见过。但在笔试里它考察的就不只是“会用 Set 而已”// 基础版利用 Set const unique arr [...new Set(arr)]; // 进阶版保留首次出现的顺序 const unique arr arr.filter((item, index) arr.indexOf(item) index); // 更通用按属性去重比如给对象数组按 id 去重 const uniqueBy (arr, key) [...new Map(arr.map(item [item[key], item])).values()];第三个版本的原理值得多说一句Map的 key 是唯一的用item[key]作为 keyitem作为 value 放进 Map后出现的同名 key 会覆盖先出现的 value所以最后values()取出来的是每个 key 对应的最后一条记录。如果希望保留第一条记录可以反着遍历。刷笔试卷时很多人会跳过这种“太简单”的题。但根据我的经验大厂的校招笔试卷恰恰喜欢在这种简单的题目上设置隐性考察点——比如要求处理超大数组时的性能、要求保持顺序、要求按对象属性去重。这些变体考察的是你“能不能把工具方法背后的原理迁移到新场景”。5.2 经典算法二叉树层次遍历与快速排序算法部分那轮笔试考了二叉树的层次遍历这也是前端面试中出现频率极高的数据结构题。因为 DOM 树本身就是一棵多叉树前端的很多场景天然适合用树结构建模。用 JavaScript 实现二叉树的层次遍历BFS最直接的方式是结合队列function levelOrder(root) { if (!root) return []; const result []; const queue [root]; while (queue.length) { const levelSize queue.length; const currentLevel []; for (let i 0; i levelSize; i) { const node queue.shift(); currentLevel.push(node.val); if (node.left) queue.push(node.left); if (node.right) queue.push(node.right); } result.push(currentLevel); } return result; }注意levelSize必须在每层开始前先取出来因为循环过程中queue长度会变化。如果直接拿queue.length作为循环次数就会出现“一层当成多轮处理”的问题。这是这道题最经典的错误点没有之一。快速排序也是高频题目。前端算法题的难度一般不会要求你手写红黑树但快排、二分查找、链表反转、二叉树遍历这类基础算法是必须达到默写水平的。function quickSort(arr) { if (arr.length 1) return arr; const pivot arr[Math.floor(arr.length / 2)]; const left []; const right []; const equal []; for (let i 0; i arr.length; i) { if (arr[i] pivot) left.push(arr[i]); else if (arr[i] pivot) right.push(arr[i]); else equal.push(arr[i]); } return [...quickSort(left), ...equal, ...quickSort(right)]; }这个版本用空间换时间简单直接适合笔试作答。面试时如果被追问可以再聊原地快排和Array.prototype.sort的内部机制V8 里数组长度小于 10 用插入排序大于等于 10 用快速排序的变体 TimSort。5.3 算法题的备考策略前端岗位不需要“奥数冠军”有些同学一想到算法就害怕觉得自己数学不好可能不适合大厂。但说实话前端校招笔试中的算法题整体难度是低于后端同级别岗位的。核心考察范围就是数组、字符串、链表、栈/队列、二叉树、哈希表、基础排序搜索、简单的动态规划如爬楼梯、斐波那契数列、最大子序和。我的备考建议是抓住高频题清单而不是盲目刷 300 道题。针对前端校招下面这张清单基本够用分类高频题目必会程度数组两数之和、三数之和、最大子序和、合并区间必须会字符串最长回文子串、无重复字符最长子串、字符串翻转必须会链表反转链表、环形链表判断、合并两个有序链表必须会树二叉树前/中/后序遍历、层次遍历、最大深度必须会排序快排、归并排序、堆排序思想必须会动态规划爬楼梯、打家劫舍、零钱兑换尽量会刷题时不要只想着“做出来”每道题做出来后多问一句时间复杂度和空间复杂度是多少有没有更优解边界条件是什么我自己复习时有一个习惯每道题都在代码旁边备注时间和空间复杂度这样笔试时写答案也会自然地带上复杂度分析阅卷人一眼就知道你是有算法素养的。6. 综合应用题与工程化笔试里的“场景题”怎么拿分6.1 前端性能优化一道说烂了但依然能拉开差距的题综合题部分是校招笔试最有区分度的地方因为它考察的是你“有没有做过东西”。百度那份卷子里有一道前端性能优化题大意是一个移动端页面首屏加载很慢从网络、渲染、资源加载三个角度分析可能原因并给出优化方案。这道题怎么答直接反映候选人的真实项目经验。光是罗列“压缩 JS、合并文件、CDN 加速”这种话术已经拿不到分了满大街都是这么写答案的。你需要的是逻辑清晰、有优先级、能自圆其说的分层方案。网络层面减少请求数量合并小文件、雪碧图那个年代还常用、HTTP/2 多路复用对请求数限制的缓解减小传输体积开启 gzip/Brotli 压缩JS/CSS 压缩混淆图片用 WebP 格式利用缓存指纹化静态资源配强缓存HTML 配协商缓存使用 CDN 把静态资源分发到离用户更近的节点。渲染层面减少首屏阻塞script标签加defer或asyncCSS 内联关键路径样式减少重排重绘合并 DOM 操作、使用requestAnimationFrame、避免频繁读取布局属性懒加载图片用loadinglazy路由级代码分割首屏只加载必要的 JS。资源层面代码分割Webpack 的splitChunks把公共依赖单独打包利用浏览器缓存Tree Shaking消除未使用的导出代码减少打包体积服务端渲染或预渲染如果 SEO 和首屏体验要求高可以考虑 SSR但这会引入额外的维护成本要结合团队情况评估。答题时我建议只写骨架但是每个点后面一定跟一句“为什么”和“怎么做”这样才能展现出你真的踩过坑。比如光写“图片懒加载”没有意义写成“首屏外的图片用loadinglazy或 IntersectionObserver 实现按需加载因为首屏图片请求会堵塞资源带宽拖慢 LCP”就完全不一样了。6.2 Webpack 构建题工程化能力的基本盘2020 年 Webpack 在前端工程化中几乎是垄断地位笔试卷考 Webpack 是常规操作。百度那份卷子有一道题大意是项目构建速度太慢你能从哪些方向优化参考答案可以从四个维度展开Loader 范围缩小include限定src目录exclude排除node_modules避免不必要的文件处理缓存babel-loader开启cacheDirectory或使用cache-loaderWebpack 5 的持久化缓存可以跨构建复用并行构建thread-loader把耗时的 loader 放到 worker 池中并行处理HappyPack当时常用现在维护少了减少模块解析resolve.alias指定库的入口文件、resolve.extensions减少无意义的后缀名尝试、module.noParse跳过不需要解析的库比如 jQuery 这类 UMD 包装的库开发时用devtool: eval-cheap-module-source-map生产构建不用 devtool 或只生成简化的 source map因为 source map 的生成非常费时间。如果你没用过 Webpack 但用过 Vite可以把 Vite 的思路套进来开发环境中使用原生 ESM 按需编译预构建依赖缓存避免全量打包。原理是相通的都是为了“少干活、干快活”。笔试里如果时间有限不要求你把所有配置项都默写出来但至少要写出一个“明确的问题定位 对应的配置项 配置后的预期效果”结构这比零散地写十个优化点强得多。6.3 场景设计题设计一个图片懒加载组件综合设计题也是大厂笔试的常客。我记得百度那轮有一道比较偏实践的题要求实现一个图片懒加载组件可以是伪代码或描述思路。要拿高分的话可以从这几个层面展开HTML/CSS 层面图片不直接设置src用>const observer new IntersectionObserver((entries) { entries.forEach(entry { if (entry.isIntersecting) { const img entry.target; img.src img.dataset.src; img.classList.add(loaded); observer.unobserve(img); } }); }, { rootMargin: 0px 0px 200px 0px, // 提前 200px 预加载 }); document.querySelectorAll(img[data-src]).forEach(img observer.observe(img));问题难点在于图片全部加载完之前要防止重复触发所以用unobserve在加载后取消观察rootMargin可以控制预加载的距离这样用户体验更好。另外要处理 JS 文件加载前页面已经滚动到目标位置的情况——需要先手动检查一次所有图片的位置而不是只靠后续滚动事件触发。这类场景题的高分秘诀是不要只写一个实现要把“为什么用 IntersectionObserver 而不是 scroll getBoundingClientRect”讲出来——前者不需要监听高频滚动事件性能更好同时要主动聊边界情况比如禁用 JS 时的降级方案noscript或直接输出src、错误加载的兜底等。这些细节才是真正打动阅卷人的地方。7. 备考复盘与建议用一份真题卷校准自己的知识体系7.1 考完别急着对答案先做“错题归因”很多同学做完笔试卷后就急着去搜标准答案对完就过了。这样其实效率很低。我更推荐的做法是每道错题都归因到知识模块再决定下一轮复习重点。比如这道题错了可能是因为JavaScript 语法细节掌握不牢作用域、闭包、异步时序框架原理只停留在 API 使用层面没看过源码算法题刷少了看到题目没有思路工程化经验不足场景题不知道从哪下手纯粹是时间分配问题简单题花太久大题没时间写。把这五类原因列出来对照自己的错题分布就能很清楚看到短板在哪。我的经验是时间分配问题最可惜也最好解决——考前做两三次完整模拟卡着时间练就能避免“前面的题写太细后面的题来不及”的尴尬。7.2 各类知识模块的优先级排序结合多份大厂校招笔试卷的共性我给准备中的同学一个优先级参考优先级知识模块备考动作第一优先JS 基础原型链、闭包、异步、this手写常见代码题第一优先浏览器与网络缓存、渲染机制、HTTP 常见状态码、跨域第二优先框架原理至少吃透一个框架的响应式/更新机制和生命周期第二优先手写代码8 道高频手写题背到肌肉记忆第三优先算法高频题刷 50-80 道掌握套路即可第三优先工程化构建优化、性能优化方案能说会道第四优先HTML/CSS少但精Flex 布局、BFC、层级问题常考这份清单基本是按“性价比”排列的——同样的复习时间投进去哪块分提升最快就优先搞哪块。HTML/CSS 不是说不用看而是它的考点相对集中出题套路固定花一天时间集中突破就够了不需要长期投入。7.3 从笔试卷反推面试准备方向最后分享一个我自己很受益的思路——笔试不只是笔试它事实上也是面试的浓缩预告。百度这份卷子暴露出的考察方向和后续的面试题高度重合。笔试考了闭包和异步面试大概率会追问“讲一下你项目中异步竞态是怎么处理的”笔试考了缓存面试官可能就会让你“描述一下从输入 URL 到页面渲染的完整过程”笔试考了手写深拷贝面试就可能问“深拷贝遇到循环引用怎么办WeakMap 和 Map 的区别是什么”。所以做完一份真题卷别急着扔掉。把里面的每一道题当成一个“考点的入口”顺着它往下追问两三层就是在为面试做准备。我当年就是靠这个方法把一次失败的笔试变成了一本自己的面试题库——每道题都延伸出三到五个追问每个追问都写成小卡片回头翻一遍比刷十篇面经都有用。回到这份百度 2020 校招 Web 前端工程师笔试卷它的价值不仅在于“公司名气大”而在于它是一份非常标准的互联网大厂前端校招能力模型样本。把它的考点彻底搞懂你的基本功、框架理解、编码能力、工程化思维都已经达到一个合格前端工程师的基准线了。剩下的就是在真实项目中继续打磨把“能做题”变成“能做事”。
返回列表