插件窝 干货文章 10个JavaScript面试常考的前端手写题总结

10个JavaScript面试常考的前端手写题总结

函数 实现 class function 819    来源:    2024-10-18

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函数

实现bind函数 JavaScript中原生的bind函数可以创建一个新的函数,在调用时设置其this上下文并传递预设参数。这里的实现同样创建了一个新的函数,并在其内部调用了原函数,同时保证了this指向和传参的正确性。

Function.prototype.myBind = function(context, ...args) {
    const self = this;
    return function(...newArgs) {
        return self.apply(context, [...args, ...newArgs]);
    };
};

4. 实现数组的map方法

实现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类型的值时直接返回,否则创建一个新的对象并递归地复制原对象的所有属性。

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)

防抖函数(debounce) 防抖函数用于限制某个函数在一定时间内只能执行一次。例如在窗口 resize 或输入框连续输入事件中,防止短时间内多次触发。这里的实现是在每次调用时清除上一次的延时任务,然后重新设置一个延时任务,只有在指定时间间隔内没有再次调用时,才会执行原函数。

function debounce(func, wait) {
    let timeout;
    return function(...args) {
        clearTimeout(timeout);
        timeout = setTimeout(() => {
            func.apply(this, args);
        }, wait);
    };
}

7. 实现节流函数(throttle)

节流函数(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的then方法接受两个回调函数作为参数,分别处理成功和失败的情况。这里模拟Promise的状态机,根据Promise当前状态异步执行相应的回调函数,并处理回调返回的新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 请求

简易版Ajax请求 实现了一个基于XMLHttpRequest的简易Ajax请求函数,返回一个Promise对象。当请求完成且状态码正常时解析响应内容并resolve,否则reject。

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. 实现一个简易版的事件委托

实现事件委托 事件委托是一种优化事件处理的方式,通过监听父级元素的事件,然后通过事件对象判断触发事件的具体子元素,从而减少绑定事件的数量。在这个实现中,当父元素接收到事件时,会向上遍历事件传播链,查找是否匹配特定选择器的目标元素,如果匹配就执行处理器函数。

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常考手写题内容请搜索插件窝以前的文章或继续浏览下面的相关文章希望大家以后多多支持插件窝!