前言继上一篇吃透 JS 核心基础后这一篇咱们直击字节面试的「重头戏」——手写代码但凡面过字节的同学都懂字节一面后半段面试官必甩过来一道手写题而且不考花里胡哨的全是「基础但抠细节」的高频题比如防抖节流、深拷贝、Promise、bind 这些。很多人栽在这一步不是不会写而是写得不够严谨、细节不到位比如深拷贝不处理循环引用、防抖不支持立即执行。这一篇我拿字节真实一面手写原题用最接地气的语言讲透每一行代码每道题都附「字节面试官想看到的细节」「高频追问」让你写出来的代码直接戳中面试官的加分点不用死记硬背理解后就能默写。真题 1手写防抖debounce—— 字节高频几乎每场都考面试原题请手写一个防抖函数要求支持立即执行比如搜索框输入时第一次输入立即触发后续频繁输入不触发停顿后再触发支持取消防抖说明防抖的应用场景以及字节业务中哪里会用到核心考察点闭包的实际应用、定时器的控制、this 指向、函数参数传递字节超爱抠这些细节通俗解析先懂原理再写代码防抖的核心就是「频繁触发时只认最后一次或者第一次」比如你疯狂点按钮防抖能让它只执行一次避免重复请求。举个生动的例子就像你给朋友发消息他正在忙你连续发了 10 条他不会每条都回只会等你停住不发了再统一回一条 —— 这就是「非立即执行」如果他看到第一条就立马回之后的消息等你停住再补回就是「立即执行」。字节标准手写代码带细节加分款js// 防抖函数fn目标函数delay延迟时间immediate是否立即执行 function debounce(fn, delay 300, immediate false) { let timer null; // 用闭包保存定时器避免每次调用都创建新的 // 这里不能用箭头函数要保留this指向字节面试官重点看 const debounced function (...args) { const context this; // 保存当前this比如DOM事件中的this指向元素 // 每次触发先清空之前的定时器重置延迟 if (timer) clearTimeout(timer); // 立即执行逻辑字节常考的细节 if (immediate) { // 只有第一次触发时才执行timer为null时就是第一次 const callNow !timer; // 无论是否立即执行都要设置定时器控制后续触发 timer setTimeout(() { timer null; // 延迟结束后重置timer允许下次立即执行 }, delay); // 第一次触发立即执行函数 if (callNow) fn.apply(context, args); } else { // 非立即执行每次触发都重新设置定时器延迟执行 timer setTimeout(() { fn.apply(context, args); // 绑定this和参数细节拉满 timer null; // 执行后重置避免内存泄漏字节加分点 }, delay); } }; // 新增取消防抖字节高频追问点必须加 debounced.cancel function () { clearTimeout(timer); timer null; // 重置避免残留定时器 }; return debounced; }字节业务应用场景面试必说加分搜索框输入联想字节搜索、抖音搜索避免输入时频繁请求接口停顿后再请求窗口 resize 事件字节后台管理系统避免窗口拖动时频繁触发布局调整按钮提交抖音评论、字节表单避免用户疯狂点击重复提交数据字节高频追问必背防抖里为什么要用 apply 绑定 this答如果不绑定fn 里的 this 会指向 window非严格模式而实际使用时比如 DOM 事件fn 的 this 应该指向触发事件的元素绑定 this 才能符合实际业务场景。为什么要给防抖函数加 cancel 方法答实际业务中可能需要手动终止防抖比如用户输入一半切换页面需要取消后续的请求这是字节面试官看重的「业务落地能力」。防抖和节流的区别下一题直接讲提前记好真题 2手写节流throttle—— 字节高频与防抖成对考面试原题请手写节流函数要求实现两种版本定时器版、时间戳版说明节流的应用场景和防抖的核心区别字节业务中哪里会用到节流核心考察点定时器 / 时间戳的应用、闭包、函数执行频率控制字节喜欢对比防抖和节流通俗解析一眼分清防抖和节流如果说防抖是「等你停了再执行」那节流就是「不管你怎么动固定时间内只执行一次」。还是举个生动的例子你在抖音刷视频手指一直往下滑视频不会每滑动一像素就加载一次而是每隔固定时间比如 300ms加载一次 —— 这就是节流控制执行频率避免过度消耗性能。版本 1定时器版字节常用易理解js// 节流函数固定时间内只执行一次函数 function throttle(fn, delay 300) { let timer null; // 用闭包保存定时器 return function (...args) { const context this; // 如果没有定时器就创建一个延迟执行 if (!timer) { timer setTimeout(() { fn.apply(context, args); timer null; // 执行后重置定时器允许下次触发 }, delay); } }; }版本 2时间戳版字节追问会问进阶款jsfunction throttle(fn, delay 300) { let lastTime 0; // 用闭包保存上一次执行的时间戳 return function (...args) { const context this; const nowTime Date.now(); // 获取当前时间戳 // 只有当前时间 - 上一次执行时间 delay才执行 if (nowTime - lastTime delay) { fn.apply(context, args); lastTime nowTime; // 更新上一次执行时间 } }; }防抖 vs 节流字节必问一句话记死防抖频繁触发只执行最后一次或第一次适合「避免重复请求」搜索、提交节流频繁触发固定时间内只执行一次适合「控制执行频率」滚动、拖拽、刷视频字节业务应用场景面试必说抖音视频滚动加载每隔固定时间加载下一批视频避免滚动过快导致大量请求字节后台拖拽组件拖拽时固定频率更新组件位置避免频繁渲染直播弹幕滚动固定频率更新弹幕位置提升页面性能字节高频追问定时器版和时间戳版节流的区别答定时器版是「延迟执行」触发后延迟 delay 执行即使停止触发也会执行最后一次时间戳版是「立即执行」触发后只要满足时间差就立即执行停止触发后不会再执行。什么时候用定时器版什么时候用时间戳版答需要「最后一次触发也能执行」比如拖拽结束后更新最终位置用定时器版不需要最后一次执行比如滚动加载停止滚动就不加载用时间戳版。真题 3手写深拷贝deepClone—— 字节重中之重抠细节到极致面试原题请手写一个深拷贝函数要求支持对象、数组、基本类型解决循环引用问题字节必考很多人栽在这里说明深拷贝和浅拷贝的区别核心考察点递归的应用、数据类型判断、循环引用处理、原型链知识字节超爱抠循环引用通俗解析先懂深浅拷贝再写代码浅拷贝就像「复制文件快捷方式」你改了快捷方式指向的文件原文件也会变深拷贝就像「复制文件本身」你改复制后的文件原文件完全不受影响。而循环引用就是「自己引用自己」比如 const obj {a: 1}; obj.b obj; 如果不处理深拷贝时会陷入无限递归直接报错 —— 这是字节面试官判断你是否懂深拷贝的关键。字节标准手写代码处理循环引用加分款js// 深拷贝函数obj要拷贝的对象map用于处理循环引用WeakMap更优避免内存泄漏 function deepClone(obj, map new WeakMap()) { // 1. 基本类型null、undefined、string、number、boolean、symbol直接返回不用拷贝 if (typeof obj ! object || obj null) { return obj; } // 2. 处理循环引用如果map里已经有这个对象直接返回避免无限递归字节核心考点 if (map.has(obj)) { return map.get(obj); } // 3. 区分数组和对象创建对应的空容器 const target Array.isArray(obj) ? [] : {}; // 4. 把当前对象存入map标记已拷贝解决循环引用 map.set(obj, target); // 5. 遍历对象/数组递归拷贝每一个属性 for (let key in obj) { // 只拷贝自身属性不拷贝原型链上的属性字节细节点 if (obj.hasOwnProperty(key)) { target[key] deepClone(obj[key], map); // 递归拷贝子属性 } } return target; }测试代码面试时可以主动写加分js// 测试基本类型 console.log(deepClone(123) 123); // true // 测试数组 const arr [1, [2, 3], { a: 4 }]; const arrClone deepClone(arr); console.log(arrClone[1] arr[1]); // false深拷贝子数组也拷贝了 // 测试循环引用字节必测 const obj { a: 1 }; obj.b obj; // 循环引用obj.b 指向 obj 本身 const objClone deepClone(obj); console.log(objClone.b objClone); // true正确处理循环引用不报错字节高频追问必背为什么用 WeakMap 处理循环引用而不用 Map答WeakMap 的键是弱引用当被引用的对象比如循环引用的 obj没有其他引用时会被垃圾回收机制回收避免内存泄漏而 Map 的键是强引用会一直占用内存导致内存泄漏。深拷贝还能怎么实现有什么缺点答JSON.parse (JSON.stringify (obj))缺点是不支持函数、undefined、symbol、循环引用所以面试时不能用这个当答案只能作为补充。浅拷贝有哪些方法答Object.assign ()、扩展运算符...、数组的 slice ()、concat ()这些都是浅拷贝只拷贝一层。真题 4手写 Promise 核心逻辑字节必问极简可背版面试原题请手写一个简易版 Promise要求支持 resolve、reject、then 方法说明 Promise 的三种状态以及状态变化规则解释 Promise 为什么能解决回调地狱核心考察点Promise 原理、异步编程、回调函数处理字节一面高频不用写太复杂核心逻辑到位即可通俗解析Promise 到底是什么Promise 就像「一个快递单」你下了订单发起异步请求快递有三种状态 —— 等待中pending、已送达fulfilled、已丢失rejected状态一旦确定就再也改不了比如已送达就不能再变成丢失。then 方法就是「快递送达后的处理方式」你可以指定 “送达后拆快递”成功回调、“丢失后联系商家”失败回调这样就不用把回调嵌套在回调里解决了回调地狱。字节简易手写版面试够用重点看逻辑js// 定义Promise的三种状态常量不可修改字节细节 const PENDING pending; const FULFILLED fulfilled; const REJECTED rejected; class MyPromise { constructor(executor) { this.status PENDING; // 初始状态等待中 this.value undefined; // 成功时的返回值 this.reason undefined; // 失败时的错误信息 // 1. 定义resolve方法将状态改为成功保存返回值 const resolve (value) { // 状态一旦改变就不能再修改字节核心规则 if (this.status ! PENDING) return; this.status FULFILLED; this.value value; }; // 2. 定义reject方法将状态改为失败保存错误信息 const reject (reason) { if (this.status ! PENDING) return; this.status REJECTED; this.reason reason; }; // 3. 执行 executor 函数传入resolve和reject捕获执行错误字节细节 try { executor(resolve, reject); } catch (err) { reject(err); // 执行出错直接reject } } // 4. 定义then方法接收成功和失败回调 then(onFulfilled, onRejected) { // 成功状态执行成功回调传入成功值 if (this.status FULFILLED) { onFulfilled(this.value); } // 失败状态执行失败回调传入错误信息 if (this.status REJECTED) { onRejected(this.reason); } } }测试代码面试时主动写加分js// 测试Promise const p new MyPromise((resolve, reject) { setTimeout(() { resolve(字节面试必过); }, 1000); }); p.then( (res) console.log(res), // 1秒后输出字节面试必过 (err) console.log(err) );字节高频追问必背Promise 的状态为什么不能改变答为了保证异步操作的结果唯一避免出现 “既成功又失败” 的情况符合异步编程的可靠性要求。Promise 怎么解决回调地狱答通过 then 方法链式调用将嵌套的回调改成线性的调用代码更清晰、更易维护避免了 “回调套回调” 的嵌套地狱。你写的这个 Promise 有什么缺点进阶追问答没有实现 then 方法的链式调用、没有处理异步 resolve/reject比如 executor 里是异步操作then 方法会先执行此时状态还是 pending不会触发回调下一篇会补充完善。本篇总结字节面试直接背字节一面手写题核心就这 4 道考频排序深拷贝 防抖节流 Promise 其他记住这 3 个关键点所有手写题一定要处理 this 指向和参数传递字节面试官最看重细节深拷贝必须处理「循环引用」用 WeakMap 是加分点防抖节流要分清区别还要能说出字节业务应用场景体现你的业务落地能力。这一篇的手写题你能默写下来字节一面的手写关基本稳过。