下次接好运~
以下是JavaScript面试中高频出现的“手撕代码”题目详解,涵盖核心原理、代码实现与面试应答要点,结合最新技术实践整理:
⚙️ 一、原型与继承
-
手撕
instanceof
原理:沿原型链向上查找,判断构造函数的prototype
是否在对象原型链上。
代码实现:function myInstanceof(obj, constructor) {let proto = obj.__proto__;while (proto) {if (proto === constructor.prototype) return true;proto = proto.__proto__;}return false; } console.log(myInstanceof([], Array)); // true
关键点:递归访问
__proto__
,边界条件为proto === null
。 -
手撕
new
操作符
原理:创建空对象 → 绑定原型 → 执行构造函数 → 返回对象。
代码实现:function myNew(constructor, ...args) {const obj = Object.create(constructor.prototype);const result = constructor.apply(obj, args);return result instanceof Object ? result : obj; } function Person(name) { this.name = name; } const p = myNew(Person, 'Tom');
🔧 二、函数与this
-
手撕
call/apply/bind
核心思路:通过临时绑定函数到上下文对象执行。
call
实现:Function.prototype.myCall = function(context, ...args) {context = context || window;const fn = Symbol();context[fn] = this;const res = context...args;delete context[fn];return res; }
区别:
apply
:第二参数为数组args
bind
:返回新函数,合并参数[...args, ...newArgs]
-
柯里化(Currying)
题目:实现add(1)(2)(3) = 6
实现:function curry(fn) {return function curried(...args) {if (args.length >= fn.length) return fn(...args);return (...more) => curried(...args, ...more);}; } const add = curry((a, b, c) => a + b + c);
⏳ 三、异步控制
-
防抖(Debounce)
场景:搜索框输入停止后触发请求。
实现:function debounce(fn, delay) {let timer;return (...args) => {clearTimeout(timer);timer = setTimeout(() => fn(...args), delay);}; }
-
节流(Throttle)
场景:滚动事件触发频率控制。
实现:function throttle(fn, interval) {let lastTime = 0;return (...args) => {const now = Date.now();if (now - lastTime >= interval) {fn(...args);lastTime = now;}}; }
-
手撕Promise
核心功能:状态机(pending/fulfilled/rejected)+ 回调队列。
简化版:class MyPromise {constructor(executor) {this.state = 'pending';this.value = null;this.onFulfilledCallbacks = [];const resolve = (value) => {if (this.state === 'pending') {this.state = 'fulfilled';this.value = value;this.onFulfilledCallbacks.forEach(cb => cb());}};executor(resolve);}then(onFulfilled) {return new MyPromise(resolve => {const wrapper = () => resolve(onFulfilled(this.value));if (this.state === 'fulfilled') wrapper();else this.onFulfilledCallbacks.push(wrapper);});} }
📚 四、数组操作
-
数组扁平化
递归实现:function flatten(arr) {return arr.reduce((pre, cur) => pre.concat(Array.isArray(cur) ? flatten(cur) : cur), []); } console.log(flatten([1, [2, [3]]])); // [1,2,3]
-
数组去重
最优解:Set
+ 扩展运算符const unique = arr => [...new Set(arr)];
-
手撕
Array.reduce
核心:处理初始值 + 遍历累加Array.prototype.myReduce = function(callback, init) {let acc = init ?? this[0];for (let i = init ? 0 : 1; i < this.length; i++) {acc = callback(acc, this[i], i, this);}return acc; }
🧩 五、对象操作
深拷贝(Deep Clone)
处理循环引用:WeakMap
存储已拷贝对象
function deepClone(obj, map = new WeakMap()) {if (obj === null || typeof obj !== 'object') return obj;if (map.has(obj)) return map.get(obj);const clone = Array.isArray(obj) ? [] : {};map.set(obj, clone);for (const key in obj) {clone[key] = deepClone(obj[key], map);}return clone;
}
💡 面试应答技巧
- 解释思路优先:先说明设计原理(如防抖的“延迟执行”),再写代码。
- 边界条件处理:主动提及
null
判断、参数校验(如reduce
的初始值)。 - 性能优化:如深拷贝用
WeakMap
避免内存泄漏。 - 对比原生API:说明手写版与原生差异(如
Promise
未实现微队列)。
⚠️ 手写题核心考察点:
- 原型链理解(如
instanceof
)- 异步控制能力(如
Promise
状态机)- 函数式编程(如柯里化)
- 内存管理意识(如深拷贝的循环引用处理)
掌握以上题目后,可覆盖90%前端面试手撕代码环节。建议实际编码测试边界用例(如数组方法处理稀疏数组)。