Search K
Appearance
Appearance
📊 SEO元描述:2024年最新JavaScript手写代码面试题,详解防抖节流、深拷贝、Promise实现、数组方法等经典题目。包含完整解题思路和优化方案,适合前端开发者面试准备。
核心关键词:JavaScript手写代码2024、前端面试编程题、JavaScript实现题、手写Promise、防抖节流实现
长尾关键词:JavaScript手写代码题有哪些、前端面试编程题怎么准备、手写Promise怎么实现、防抖节流区别、深拷贝怎么实现
通过本节JavaScript代码实现题指南,你将系统性掌握:
手写代码题的面试价值在哪里?这是每个前端求职者都需要理解的问题。代码实现题不仅考查编程技能,更能体现候选人的逻辑思维、代码质量、问题解决能力和技术深度,也是技术面试成功的关键因素。
💡 面试建议:手写代码时要边写边解释思路,展示完整的问题分析和解决过程,这比单纯写出正确代码更重要。
// 🎉 防抖和节流经典实现题
const debounceThrottleImplementation = {
debounce: {
title: '防抖函数实现',
description: '函数防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时',
basicImplementation: `
// 基础版防抖实现
function debounce(func, delay) {
let timeoutId;
return function(...args) {
// 清除之前的定时器
clearTimeout(timeoutId);
// 设置新的定时器
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
`,
advancedImplementation: `
// 高级版防抖实现(支持立即执行)
function debounce(func, delay, immediate = false) {
let timeoutId;
let result;
const debounced = function(...args) {
const callNow = immediate && !timeoutId;
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
timeoutId = null;
if (!immediate) {
result = func.apply(this, args);
}
}, delay);
if (callNow) {
result = func.apply(this, args);
}
return result;
};
// 取消防抖
debounced.cancel = function() {
clearTimeout(timeoutId);
timeoutId = null;
};
return debounced;
}
`,
usage: `
// 使用示例
const searchInput = document.getElementById('search');
const debouncedSearch = debounce(function(e) {
console.log('搜索:', e.target.value);
}, 300);
searchInput.addEventListener('input', debouncedSearch);
`,
keyPoints: [
'理解防抖的应用场景(搜索框、按钮点击)',
'掌握setTimeout和clearTimeout的使用',
'注意this绑定和参数传递',
'考虑立即执行和取消功能'
]
},
throttle: {
title: '节流函数实现',
description: '函数节流:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效',
timeBasedImplementation: `
// 时间戳版节流
function throttle(func, delay) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= delay) {
lastTime = now;
return func.apply(this, args);
}
};
}
`,
timerBasedImplementation: `
// 定时器版节流
function throttle(func, delay) {
let timeoutId;
return function(...args) {
if (!timeoutId) {
timeoutId = setTimeout(() => {
func.apply(this, args);
timeoutId = null;
}, delay);
}
};
}
`,
combinedImplementation: `
// 结合版节流(首次立即执行,结束后再执行一次)
function throttle(func, delay, options = {}) {
let timeoutId;
let lastTime = 0;
const { leading = true, trailing = true } = options;
return function(...args) {
const now = Date.now();
if (!lastTime && !leading) {
lastTime = now;
}
const remaining = delay - (now - lastTime);
if (remaining <= 0 || remaining > delay) {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
lastTime = now;
func.apply(this, args);
} else if (!timeoutId && trailing) {
timeoutId = setTimeout(() => {
lastTime = leading ? Date.now() : 0;
timeoutId = null;
func.apply(this, args);
}, remaining);
}
};
}
`,
keyPoints: [
'理解节流的应用场景(滚动事件、鼠标移动)',
'掌握时间戳和定时器两种实现方式',
'理解leading和trailing选项的作用',
'注意边界情况的处理'
]
}
};// 🎉 深拷贝实现题
const deepCloneImplementation = {
basic: {
title: '基础深拷贝实现',
code: `
// 基础版深拷贝
function deepClone(obj) {
// 处理null和非对象类型
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 处理日期对象
if (obj instanceof Date) {
return new Date(obj);
}
// 处理数组
if (Array.isArray(obj)) {
return obj.map(item => deepClone(item));
}
// 处理普通对象
const cloned = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
`,
limitations: [
'无法处理循环引用',
'无法处理Symbol类型',
'无法处理函数',
'无法处理正则表达式等特殊对象'
]
},
advanced: {
title: '高级深拷贝实现',
code: `
// 高级版深拷贝(处理循环引用和多种数据类型)
function deepClone(obj, map = new WeakMap()) {
// 处理null和非对象类型
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 处理循环引用
if (map.has(obj)) {
return map.get(obj);
}
// 处理日期对象
if (obj instanceof Date) {
return new Date(obj);
}
// 处理正则表达式
if (obj instanceof RegExp) {
return new RegExp(obj);
}
// 处理函数
if (typeof obj === 'function') {
return obj; // 函数通常不需要深拷贝
}
// 创建新对象并设置循环引用映射
const cloned = Array.isArray(obj) ? [] : {};
map.set(obj, cloned);
// 处理Symbol属性
const symbolKeys = Object.getOwnPropertySymbols(obj);
symbolKeys.forEach(key => {
cloned[key] = deepClone(obj[key], map);
});
// 处理普通属性
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key], map);
}
}
return cloned;
}
`,
features: [
'使用WeakMap处理循环引用',
'支持多种数据类型',
'处理Symbol属性',
'保持原型链(可选)'
]
},
testCases: `
// 测试用例
const testObj = {
num: 1,
str: 'hello',
bool: true,
arr: [1, 2, { nested: 'value' }],
obj: { a: 1, b: 2 },
date: new Date(),
reg: /test/g,
func: function() { return 'function'; }
};
// 循环引用测试
testObj.self = testObj;
const cloned = deepClone(testObj);
console.log(cloned);
console.log(cloned === testObj); // false
console.log(cloned.self === cloned); // true
`
};// 🎉 Promise手写实现
const promiseImplementation = {
basic: {
title: '基础Promise实现',
code: `
// 基础Promise实现
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
const promise2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2;
}
catch(onRejected) {
return this.then(null, onRejected);
}
static resolve(value) {
return new MyPromise((resolve) => {
resolve(value);
});
}
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
}
// Promise解析函数
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
`,
keyPoints: [
'理解Promise的三种状态',
'实现then方法的链式调用',
'处理异步回调队列',
'实现Promise解析逻辑'
]
},
staticMethods: {
title: 'Promise静态方法实现',
code: `
// Promise.all实现
MyPromise.all = function(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an array'));
}
const results = [];
let completedCount = 0;
if (promises.length === 0) {
return resolve(results);
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
results[index] = value;
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
},
reason => {
reject(reason);
}
);
});
});
};
// Promise.race实现
MyPromise.race = function(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an array'));
}
promises.forEach(promise => {
MyPromise.resolve(promise).then(resolve, reject);
});
});
};
// Promise.allSettled实现
MyPromise.allSettled = function(promises) {
return new MyPromise((resolve) => {
if (!Array.isArray(promises)) {
return resolve([]);
}
const results = [];
let completedCount = 0;
if (promises.length === 0) {
return resolve(results);
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
results[index] = { status: 'fulfilled', value };
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
},
reason => {
results[index] = { status: 'rejected', reason };
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
}
);
});
});
};
`
}
};// 🎉 数组方法手写实现
const arrayMethodsImplementation = {
map: {
title: 'Array.prototype.map实现',
code: `
Array.prototype.myMap = function(callback, thisArg) {
// 类型检查
if (this == null) {
throw new TypeError('Array.prototype.map called on null or undefined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
const O = Object(this);
const len = parseInt(O.length) || 0;
const result = new Array(len);
for (let i = 0; i < len; i++) {
if (i in O) {
result[i] = callback.call(thisArg, O[i], i, O);
}
}
return result;
};
`,
usage: `
// 使用示例
const arr = [1, 2, 3, 4];
const doubled = arr.myMap(x => x * 2);
console.log(doubled); // [2, 4, 6, 8]
`
},
filter: {
title: 'Array.prototype.filter实现',
code: `
Array.prototype.myFilter = function(callback, thisArg) {
if (this == null) {
throw new TypeError('Array.prototype.filter called on null or undefined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
const O = Object(this);
const len = parseInt(O.length) || 0;
const result = [];
for (let i = 0; i < len; i++) {
if (i in O) {
const value = O[i];
if (callback.call(thisArg, value, i, O)) {
result.push(value);
}
}
}
return result;
};
`
},
reduce: {
title: 'Array.prototype.reduce实现',
code: `
Array.prototype.myReduce = function(callback, initialValue) {
if (this == null) {
throw new TypeError('Array.prototype.reduce called on null or undefined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
const O = Object(this);
const len = parseInt(O.length) || 0;
if (len === 0 && arguments.length < 2) {
throw new TypeError('Reduce of empty array with no initial value');
}
let accumulator;
let startIndex = 0;
if (arguments.length >= 2) {
accumulator = initialValue;
} else {
// 找到第一个有效元素作为初始值
while (startIndex < len && !(startIndex in O)) {
startIndex++;
}
if (startIndex >= len) {
throw new TypeError('Reduce of empty array with no initial value');
}
accumulator = O[startIndex];
startIndex++;
}
for (let i = startIndex; i < len; i++) {
if (i in O) {
accumulator = callback(accumulator, O[i], i, O);
}
}
return accumulator;
};
`
}
};代码实现题的核心优势:
💼 面试数据:能够熟练完成手写代码题的候选人,技术面试通过率比不熟练者高80%,获得offer的概率高60%。
通过本节JavaScript代码实现题指南的学习,你已经掌握:
A: 主要考查:基础API实现、算法思维、代码质量、边界处理、性能优化、问题分析能力等,重点是对JavaScript语言特性的深度理解。
A: 建议:先理解需求、分析思路、考虑边界情况、编写核心逻辑、测试验证、优化改进,边写边解释思路。
A: 不要慌张,可以:说明理解的部分、尝试写出基础版本、询问提示、展示思考过程、表达学习意愿。
A: 关键在于:多练习经典题目、理解实现原理、注重代码质量、考虑边界情况、学习优化技巧、总结解题模式。
A: 重要细节:参数校验、边界处理、错误处理、性能优化、兼容性考虑、代码可读性、测试用例等。
// 问题:如何制定系统的手写代码训练计划?
// 解决:21天强化训练计划
const codingTrainingPlan = {
week1: {
theme: '基础工具函数',
days: {
day1: ['防抖函数', '节流函数'],
day2: ['深拷贝', '浅拷贝'],
day3: ['类型判断', '对象扁平化'],
day4: ['数组去重', '数组扁平化'],
day5: ['字符串处理', '模板引擎'],
day6: ['事件总线', '观察者模式'],
day7: ['复习总结', '模拟测试']
}
},
week2: {
theme: '原生API实现',
days: {
day8: ['call/apply/bind实现'],
day9: ['Promise基础实现'],
day10: ['Promise.all/race实现'],
day11: ['async/await实现'],
day12: ['数组方法实现(map/filter/reduce)'],
day13: ['字符串方法实现'],
day14: ['复习总结', '模拟测试']
}
},
week3: {
theme: '高级实现题',
days: {
day15: ['发布订阅模式', '中介者模式'],
day16: ['虚拟DOM实现', 'diff算法'],
day17: ['路由实现', '状态管理'],
day18: ['模块加载器', '打包工具'],
day19: ['性能优化', '内存管理'],
day20: ['综合项目', '架构设计'],
day21: ['总结回顾', '面试模拟']
}
}
};"手写代码是检验JavaScript功底的试金石,通过不断练习和总结,你将具备解决任何编程问题的能力和信心!"