10个JavaScript面试常考的前端手写题总结
本文汇总了前端面试中高频出现的10个JavaScript手写代码题,涵盖对象判空、数组去重、函数绑定、深拷贝、防抖节流、Promise及Ajax等核心知识点。通过解析原生API的实现原理,帮助开发者深入理解JavaScript运行机制,提升面试通过率与底层编码能力。
1. 实现一个简单的 JavaScript 函数,用于判断一个对象是否为空
该函数的核心逻辑是遍历目标对象的所有自有属性,检测是否存在任何实际定义的键值。若在遍历过程中发现至少一个有效键,则立即返回 false,表明对象非空;若循环完整执行完毕仍未找到任何键,则返回 true,确认对象为空。
function isEmptyObject(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
return false;
}
}
return true;
}
2. 手写数组去重
数组去重提供了两种常见实现思路。第一种采用传统遍历方式,初始化一个结果数组,利用 indexOf 方法逐一检查当前元素是否已存在于结果集中,若不存在则将其追加至结果数组。第二种方案借助 ES6 引入的 Set 数据结构特性,利用其自动去重的机制,将数组转换为 Set 后再通过展开运算符转回数组,从而实现简洁高效的去重。
function unique(arr) {
let res = [];
for(let i = 0; i < arr.length; i++) {
if(res.indexOf(arr[i]) === -1) {
res.push(arr[i]);
}
}
return res;
}
// 或者使用ES6的新特性Set
function uniqueES6(arr) {
return [...new Set(arr)];
}
3. 实现bind函数
JavaScript 原生 bind 方法用于创建新函数,并预设调用时的 this 上下文及初始参数。此实现同样返回一个新函数,在内部通过 apply 方法调用原函数,确保 this 指向被正确绑定,且预设参数与后续传入参数能够按顺序合并传递。
Function.prototype.myBind = function(context, ...args) {
const self = this;
return function(...newArgs) {
return self.apply(context, [...args, ...newArgs]);
};
};
4. 实现数组的map方法
Array.prototype.map() 用于生成一个新数组,新数组中的元素由原数组每个元素调用提供的回调函数后返回的结果组成。模拟实现时,需遍历原数组,将回调函数作用于每个元素(同时传递索引和原数组引用),并将回调返回值依次推入新数组,最终返回该新数组。
Array.prototype.myMap = function(callback, thisArg) {
const newArray = [];
for (let i = 0; i < this.length; i++) {
newArray.push(callback.call(thisArg, this[i], i, this));
}
return newArray;
};
5. 实现简易版的深拷贝
简易深拷贝通过递归逻辑实现。函数首先判断输入值是否为对象且不为 null,若为非对象类型或 null 则直接返回原值。否则,根据原对象类型初始化新对象(数组或普通对象),并遍历原对象的所有自有属性,对每个属性值递归调用拷贝函数,最终返回包含完整嵌套结构的新对象。
function deepClone(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
let cloneObj = Array.isArray(obj) ? [] : {};
for(let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key]);
}
}
return cloneObj;
}
6. 实现防抖函数(debounce)
防抖函数旨在限制函数在指定时间间隔内仅执行一次,常用于窗口 resize 或输入框连续输入等高频触发场景。实现原理是每次调用时清除上一次的定时器,并重新设置一个新的定时器。只有当指定时间间隔内没有再次触发调用时,定时器才会到期并执行原函数,从而避免频繁执行。
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, args);
}, wait);
};
}
7. 实现节流函数(throttle)
节流函数确保在持续触发事件中,函数每隔固定时间间隔执行一次。与防抖不同,节流保证在高频调用下仍能按节奏执行。实现时记录上一次执行的时间戳,每次触发时计算当前时间与上次执行时间的差值;若差值大于设定的时间间隔,则执行函数并更新时间戳,否则忽略本次调用。
function throttle(func, delay) {
let prev = Date.now();
return function(...args) {
const now = Date.now();
if (now - prev >= delay) {
func.apply(this, args);
prev = now;
}
};
}
8. 实现 Promise 的 then 方法
Promise 的 then 方法接收成功和失败两个回调函数。实现时需模拟 Promise 的状态机机制,根据当前状态(pending、fulfilled、rejected)异步执行相应的回调。同时需处理回调函数可能返回新的 Promise 的情况,通过 resolvePromise 逻辑确保链式调用的正确性,即新 Promise 的状态由回调返回值决定。
MyPromise.prototype.then = function(onFulfilled, onRejected) {
let self = this;
return new MyPromise((resolve, reject) => {
if (self.status === 'fulfilled') {
setTimeout(() => { // 异步执行
try {
let x = onFulfilled(self.value);
resolvePromise(x, resolve, reject);
} catch(e) {
reject(e);
}
}, 0);
} else if (self.status === 'rejected') {
setTimeout(() => {
try {
let x = onRejected(self.reason);
resolvePromise(x, resolve, reject);
} catch(e) {
reject(e);
}
}, 0);
} else {
self.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(self.value);
resolvePromise(x, resolve, reject);
} catch(e) {
reject(e);
}
}, 0);
});
self.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(self.reason);
resolvePromise(x, resolve, reject);
} catch(e) {
reject(e);
}
}, 0);
});
}
});
};
function resolvePromise(value, resolve, reject) {
if (value instanceof MyPromise) {
value.then(resolve, reject);
} else {
resolve(value);
}
}9. 实现简易版 Ajax 请求
基于 XMLHttpRequest 的简易 Ajax 封装返回一个 Promise 对象。请求发起后,监听 readyState 变化,当请求完成且状态码为 2xx 时,解析响应内容并 resolve;若发生错误或状态码异常,则 reject 错误信息。通过 Promise 封装,使得异步请求处理更加符合现代 JavaScript 的异步编程范式。
function ajax(url, method, data) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(xhr.statusText);
}
}
};
xhr.onerror = function() {
reject(xhr.statusText);
};
xhr.send(JSON.stringify(data));
});
}10. 实现一个简易版的事件委托
事件委托通过监听父级元素的事件来优化性能,减少子元素的事件绑定数量。实现时,当父元素接收到事件后,通过 event.target 获取实际触发事件的元素,并沿事件传播链向上遍历,检查是否匹配特定的选择器或条件。若匹配,则执行对应的处理器函数,从而实现高效的事件处理。
function delegateEvent(element, selector, eventType, handler) {
element.addEventListener(eventType, function(event) {
let target = event.target;
while (target && target !== this) {
if (target.matches(selector)) {
handler.call(target, event);
break;
}
target = target.parentNode;
}
});
}到此这篇关于10个JavaScript面试常考的前端手写题总结的文章就介绍到这了,更多相关JavaScript常考手写题内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
React中怎么不可变地删除JS数组元素
2026-11-05 18:06:01
JS动态构建复杂嵌套JSON对象的方法
2026-11-05 15:41:52
JS负数取余为什么是负数?详解余数与模运算区别
2026-11-05 13:34:41
JS 解压出现乱码或报错怎么解决?
2026-11-05 12:32:38
JS 中 startsWith、indexOf 与正则判断字符串前缀的对比
2026-11-04 22:19:43
