web前端手撕合集

这篇文章包含了一些前端常见的手撕题,和部分我在面试过程中遇到的手撕题。非算法题。

防抖

function debounce(fn, delay, immediate = false) {
    let timer
    let flag = true

    return function () {
        if (flag && immediate) {
            fn.apply(this, arguments)
            flag = false
            return
        }
        clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, arguments)
        }, delay)
    }
}

节流

function throttle(fn, interval) {
    let last = 0

    return function () {
        if (Date.now() - last < interval) return
        last = Date.now()
        fn.apply(this, arguments)
    }
}

柯里化

function curry(fn, ...args1) {
    if (args1.length >= fn.length) {
        return fn(...args1)
    } else {
        return (...args2) => curry(fn, ...args1, ...args2)
    }
}

浅拷贝

function shallowCopy(obj) {
    return Array.isArray(obj) ? [...obj] : { ...obj } // or
    return Object.assign(Array.isArray(obj) ? [] : {}, obj)
}

深拷贝

function deepClone(o, wm = new WeakMap()) {
    if (wm.has(o)) return wm.get(o)

    let co = Array.isArray(o) ? [] : {}
    typeof o === 'object' && o !== null && wm.set(o, co)

    for (let k of Object.getOwnPropertySymbols(o)) {
        if (typeof o[k] === 'object' && o[k] !== null) {
            co[k] = deepClone(o[k], wm)
        } else {
            co[k] = o[k]
        }
    }
    for (let k in o) {
        if (o.hasOwnProperty(k)) {
            if (typeof o[k] === 'object' && o[k] !== null) {
                co[k] = deepClone(o[k], wm)
            } else {
                co[k] = o[k]
            }
        }
    }

    return co
}

数组最大深度

function getDepth(ele) {
    if (!Array.isArray(ele)) {
        return 0
    }
    let maxDepth = 0
    ele.forEach(v => {
        maxDepth = Math.max(maxDepth, getDepth(v))
    })
    return maxDepth + 1
}

数组flat

Array.prototype.myFlat = function (depth = 1) {
    if (depth <= 0) return [...this];
    return this.reduce((p, c) => {
        if (Array.isArray(c)) {
            p.push(...c.myFlat(depth - 1))
        } else {
            p.push(c)
        }
        return p
    }, [])
}

数组reduce

Array.prototype.myReduce = function (cb, initial) {
    let res = initial !== undefined ? initial : this[0]
    let start = initial !== undefined ? 0 : 1
    for (let i = start; i < this.length; ++i) {
        res = cb(res, this[i])
    }
    return res
}

new

function myNew(con, ...args) {
    if (!con instanceof Function) {
        throw new Error()
    }
    // let obj = {}
    // obj.__proto__ = con.prototype
    // or
    let obj = null
    obj = Object.create(con.prototype)
    let result = con.apply(obj, args)
    let flag = result && (typeof result === 'object' || typeof result === 'function')
    return flag ? result : obj
}

call & apply

Function.prototype.myCall = function (context, ...args) {
    context = context === null || context === undefined ? globalThis : Object(context)
    const fnk = Symbol()
    Object.defineProperty(context, fnk, { enumerable: false, value: this })
    const r = context[fnk](...args)
    delete context[fnk]
    return r
}

Function.prototype.myApply = function (context, args) {
    context = context === null || context === undefined ? globalThis : Object(context)
    const fnk = Symbol()
    Object.defineProperty(context, fnk, { enumerable: false, value: this })
    const r = context[fnk](...args)
    delete context[fnk]
    return r
}

bind

Function.prototype.myBind = function (context, ...args) {
    context = context === null || context === undefined ? globalThis : Object(context)
    const func = this

    return function (...subArgs) {
        const allArgs = [...args, ...subArgs]
        if (new.target) {
            return new func(...allArgs)
        } else {
            return func.apply(context, allArgs)
        }
    }
}

create

function myCreate(obj) {
    function F() { }
    F.prototype = obj
    return new F()
}

instanceof

function myInstanceof(obj, con) {
    let pro = obj?.__proto__
    while (pro) {
        if (pro === con.prototype) {
            return true
        }
        pro = pro.__proto__
    }
    return false
}

Promise.all

Promise.myAll = function (promiseArr) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(promiseArr)) {
            throw new Error('argument should be array')
        }
        let count = 0,
            promiseArrLength = promiseArr.length,
            resolved = []
        for (let i = 0; i < promiseArr.length; ++i) {
            Promise.resolve(promiseArr[i]).then((res) => {
                resolved[i] = res
                ++count
                if (count === promiseArrLength) {
                    resolve(resolved)
                }
            }, (rej) => {
                reject(rej)
            })
        }
    })
}

Promise.race

Promise.myRace = function (promiseArr) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(promiseArr)) {
            throw new Error('argument should be array');
        }
        promiseArr.forEach(promise => {
            Promise.resolve(promise).then(resolve, reject)
        })
    })
}

Promise.retry

Promise.retry = function (fn, retries) {
    return new Promise((resolve, reject) => {
        Promise.resolve(fn())
            .then(resolve)
            .catch(error => {
                if (retries > 0) {
                    console.log(`Retrying... ${retries} attempts left`);
                    Promise.retry(fn, retries - 1)
                        .then(resolve)
                        .catch(reject);
                } else {
                    reject(error);
                }
            });
    })
}

Promise.queue

Promise.queue = function (promiseArr) {
    return new Promise((resolve, reject) => {
        const resultArr = [];
        // (async () => {
        //     for (let i = 0; i < promiseArr.length; i++) {
        //         try {
        //             resultArr[i] = await promiseArr[i]()
        //         } catch (err) {
        //             reject(err)
        //             return
        //         }
        //     }
        //     resolve(resultArr)
        // })()
        // or
        function runPromise(index) {
            if (index >= promiseArr.length) {
                resolve(resultArr)
                return
            }
            promiseArr[index]().then(res => {
                resultArr[index] = res
                runPromise(index + 1)
            }, err => {
                reject(err)
                return
            })
        }
        runPromise(0)
    })
}

promisify

function promisify(func) {
    return function (...args) {
        return new Promise((resolve, reject) => {
            func(...args, (err, result) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(result);
                }
            });
        });
    };
}

Event Bus

class EventBus {
    constructor() {
        this.events = {}
    }

    on(event, callback) {
        if (this.events[event] === undefined) {
            this.events[event] = new Set()
        }
        this.events[event].add(callback)
    }

    off(event, callback) {
        if (this.events[event]) {
            this.events[event].delete(callback)
            if (!this.events[event].size) delete this.events[event]
        }
    }

    emit(event, ...args) {
        if (this.events[event]) {
            this.events[event].forEach(cb => {
                cb(...args)
            })
        }
    }

    once(event, callback) {
        const onceCallback = (...args) => {
            this.off(event, onceCallback)
            callback(...args)
        }
        this.on(event, onceCallback)
    }
}

sumOf

function sum(...args1) {
    const ret = (...args2) => sum(...args1, ...args2)
    ret.sumOf = () => args1.reduce((acc, cur) => acc + cur, 0);
    return ret
}

console.log(sum(1, 2, 3).sumOf()); // 6
console.log(sum(1, 2)(3).sumOf()); // 6
console.log(sum(1)(2)(3).sumOf()); // 6

hardMan

hardMan('潘潘')

Hi! I am 潘潘.

hardMan('潘潘').study('敲码')

Hi! I am 潘潘. I am studying 敲码.

hardMan('潘潘').rest(3).study('敲码')

Hi! I am 潘潘. 【等待三秒】 I am studying 敲码.

hardMan('潘潘').restFirst(3).study('敲码')

【等待三秒】 Hi! I am 潘潘. I am studying 敲码.

function hardMan(name) {
    this.name = name;
    this.queue = []
    this.queue.push(() => {
        return new Promise(resolve => {
            console.log(`Hi! I am ${this.name}`)
            resolve()
        })
    })
    this.rest = (sec) => {
        this.queue.push(() => new Promise(resolve => {
            setTimeout(resolve, sec * 1000)
        }))
        return this
    }
    this.restFirst = (sec) => {
        this.queue.unshift(() => new Promise(resolve => {
            setTimeout(resolve, sec * 1000)
        }))
        return this
    }
    this.study = (name) => {
        this.queue.push(() => new Promise(resolve => {
            console.log(`I am studying ${name}`)
            resolve()
        }))
        return this
    }
    this._run = () => {
        if (this.queue.length) {
            const task = this.queue.shift()
            task().then(() => {
                this._run()
            })
        }
    }
    setTimeout(this._run)
    return this
}

限制最大并发

class Schedular {
    constructor() {
        this.tasks = [];
        this.runningTasks = 0;
        this.maxRunningTasks = 2;
    }

    add(task) {
        return new Promise((resolve) => {
            this.tasks.push([task, resolve]);
            this._run();
        });
    }

    _run() {
        if (this.runningTasks >= this.maxRunningTasks || !this.tasks.length) return
        this.runningTasks++;
        const [task, resolve] = this.tasks.shift();
        task().then(() => {
            this.runningTasks--;
            resolve();
            this._run();
        })
    }
}

const timeout = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

const scheduler = new Schedular();
const addTask = (time, order) => {
    scheduler.add(() => timeout(time)).then(() => console.log(order))
}

addTask(1000, 1);
addTask(1000, 2);
// 1秒后输出12
addTask(1000, 3);
addTask(1000, 4);
// 再1秒后输出34

带优先级的fetch

class MyFetch {
    constructor() {
        this.queue = []
        this.count = 0
        this.maxCount = 5
    }

    fetch(url, options = {}, priority = 0) {
        return new Promise((resolve, reject) => {
            this.queue.push([priority, url, options, resolve, reject])
            this._run()
        })
    }

    _run() {
        if (this.count >= this.maxCount || this.queue.length === 0) return
        this.count++
        this.queue.sort((a, b) => b[0] - a[0])
        const [_, url, options, resolve, reject] = this.queue.shift()
        fetch(url, options).then(resolve).catch(reject).finally(() => {
            this.count--
            this._run()
        })
    }
}

重试函数 runWithRetry

重试函数:实现一个重试函数runWithRetry(handler: Function, times: number = 1, timeout?: number),支持异步

function runWithRetry(handler, times = 1, timeout) {
    return new Promise((resolve, reject) => {
        let timer;
        let timeouted = false;
        if (timeout !== undefined) {
            timer = setTimeout(() => {
                reject();
                timeouted = true;
            }, timeout);
        }
        function retry(times) {
            if (times <= 0) {
                clearTimeout(timer);
                reject();
                return;
            }
            Promise.resolve(handler()).then(
                (res) => {
                    clearTimeout(timer);
                    resolve(res);
                },
                () => {
                    if (!timeouted) retry(times - 1);
                },
            );
        }
        retry(times);
    });
}
#前端##面经#
全部评论
进我的收藏夹吃灰去吧
1 回复 分享
发布于 11-16 00:43 山东
m
点赞 回复 分享
发布于 11-15 23:32 湖北
阿里嘎多!
点赞 回复 分享
发布于 11-16 21:46 北京
你是我的神
点赞 回复 分享
发布于 11-19 10:08 上海
窝尿标点
点赞 回复 分享
发布于 11-26 09:29 湖南
nb,搬进我博客整理的面试题了
点赞 回复 分享
发布于 11-28 16:26 江西

相关推荐

不愿透露姓名的神秘牛友
11-25 17:46
点赞 评论 收藏
分享
评论
25
123
分享
牛客网
牛客企业服务