以下是JavaScript面试中高频出现的"手撕代码"题目详解,涵盖核心原理、代码实现与面试应答要点,结合最新技术实践整理:
⚙️ 一、原型与继承
-
手撕
instanceof
原理 :沿原型链向上查找,判断构造函数的prototype
是否在对象原型链上。
代码实现:javascriptfunction 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
操作符
原理 :创建空对象 → 绑定原型 → 执行构造函数 → 返回对象。
代码实现:javascriptfunction 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
实现:javascriptFunction.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
实现:javascriptfunction 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)
场景 :搜索框输入停止后触发请求。
实现:javascriptfunction debounce(fn, delay) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); }; }
-
节流(Throttle)
场景 :滚动事件触发频率控制。
实现:javascriptfunction throttle(fn, interval) { let lastTime = 0; return (...args) => { const now = Date.now(); if (now - lastTime >= interval) { fn(...args); lastTime = now; } }; }
-
手撕Promise
核心功能 :状态机(pending/fulfilled/rejected)+ 回调队列。
简化版:javascriptclass 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); }); } }
📚 四、数组操作
-
数组扁平化
递归实现:javascriptfunction 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
+ 扩展运算符javascriptconst unique = arr => [...new Set(arr)];
-
手撕
Array.reduce
核心:处理初始值 + 遍历累加javascriptArray.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
存储已拷贝对象
javascript
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%前端面试手撕代码环节。建议实际编码测试边界用例(如数组方法处理稀疏数组)。