前端八股之手写代码

Web前端八股文
2025-03-13 - 16:32
四季柚子
2025-03-13 - 16:32

手写代码题

1. Object.create

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

2. instanceof

function myInstanceof(left, right) {
    let proto = Object.getPrototypeOf(left)
    const prototype = right.prototype
    while (true) {
        if (!proto) {
            return false
        }
        if(proto === prototype) {
            return true
        }
        proto = Object.getPrototypeOf(proto)
    }
}

3. new操作符

function myNew(func, ...args) {
    const obj = Object.create(Object.getPrototypeOf(func))
    const result = func.apply(obj, args)
    return result instanceof Object ? result : obj
}

4. Promise

const pending = 'pending'
const resolved = 'resolved'
const rejected = 'rejected'

class Promise {
    constructor(fn) {
        this.state = pending
        this.value = null
        this.resolvedCallbacks = []
        this.rejectedCallbacks = []
        try {
            fn(this.resolve.bind(this), this.reject.bind(this))
        } catch {
            this.reject(e)
        }
    }

    resolve(value) {
        if (value instanceof Promise) {
            return value.then(this.resolve, this.reject)
        }
        // 保证代码执行顺序在本轮事件循环末尾
        setTimeout(() => {
            if (this.state === pending) {
                this.state = resolved
                this.value = value
                this.resolvedCallbacks.forEach(cb => cb(value))
            }
        }, 0)
    }

    reject(value) {
        setTimeout(() => {
            if (this.state === pending) {
                this.state = rejected
                this.value = value
                this.rejectedCallbacks.forEach(cb => cb(value))
            }
        }, 0)
    }

    then(onResolved, onRejected) {
        onResolved = typeof onResolved === 'function' ? onResolved : value => value
        onRejected = typeof onRejected === 'function' ? onRejected : err => throw err
        return new Promise((resolve, reject) => {
            if (this.state === pending) {
                this.resolvedCallbacks.push(onResolved)
                this.rejectedCallbacks.push(onRejected)
            } else if (this.state === resolved) {
                try {
                    const result = onResolved(this.value)
                    if (result instanceof Promise) {
                        result.then(v => resolve(v), e => reject(e))
                    }
                } catch (e) {
                    reject(e)
                }
            } else if (this.state === rejected) {
                onRejected(this.value)
            }
        })
    }
}

5. Promise.all

Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(promises) || !promises.every(promise => promise instanceof Promise)) {
            reject(new TypeError('Invalid Arguments'))
        }
        const result = []
        let completeCount = 0
        promises.forEach((promise, index) => {
            Promise.resolve(promise)
                .then(value => {
                    result[index] = value
                    completeCount++
                    if (completeCount === promises.length) {
                        resolve(result)
                    }
                })
                .catch(err => {
                    reject(err)
                })
        })
    })
}

6. Promise.race

Promise.race = function (promises) {
    if (!Array.isArray(promises) || !promises.every(promise => promise instanceof Promise)) {
        return Promise.reject(new TypeError('Invalid Arguments'))
    }
    return new Promise((resolve, reject) => {
        promises.forEach(promise => {
            Promise.resolve(promise).then(resolve).catch(reject)
        })
    })
}

7. 防抖函数

function debounce(fn, delay) {
    let timer = null
    return function (...args) {
        const context = this
        clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(context, args)
        }, delay)
    }
}

8. 节流函数

function throttle(fn, delay) {
    let timer = null
    let startTime = Date.now()
    return function (...args) {
        const context = this
        const curTime = Date.now()
        const remaining = delay - (curTime - startTime)
        clearTimeout(timer)
        if (remaining <= 0) {
            fn.apply(context, args)
            startTime = Date.now()
        } else {
            timer = setTimeout(() => {
                fn.apply(context, args)
            }, remaining)
        }
    }
}

9. Function.prototype.call

Function.prototype.call = function(context, ...args) {
    context = context ?? window
    context.fun = this
    const result = context.fun(...args)
    delete context.fun
    return result
}

10. Function.prototype.apply

Function.prototype.apply = function(context, args) {
    context = context ?? window
    context.fun = this
    const result = context.fun(...args)
    delete context.fun
    return result
}

11. Function.prototype.bind

Function.prototype.bind = function(context, ...args) {
    const context = this
    return function(...rest) {
        return context.call(context, ...args, ...rest)
    }
}

12. 函数柯里化

function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn.call(this, ...args)
        }
        return function(...moreArgs) {
            return curried.call(this, ...args, ...moreArgs)
        }
    }
}

13. 浅拷贝

function clone(obj) {
    if (object == null || typeof obj !== 'object') {
        return obj
    }
    if (Array.isArray(obj)) {
        const copy = []
        for (const item of obj) {
            copy.push(item)
        }
        return copy
    }
    const copy = {}
    for (const key of Object.keys(obj)) {
        copy[key] = obj[key]
    }
    return copy
}

14. 深拷贝

function deepClone(obj, map = new WeakMap()) {
    if (obj == null || typeof obj !== 'object') {
        return obj
    }
    if (map.has(obj)) {
        return map.get(obj)
    }
    if (Array.isArray(obj)) {
        const copy = []
        map.set(obj, copy)
        for (const item of obj) {
            copy.push(deepClone(item, map))
        }
        return copy
    }
    if (obj instanceof Date) {
        return new Date(obj.getTime())
    }
    if (obj instanceof RegExp) {
        return new RegExp(obj.source, obj.flags)
    }
    const copy = Object.create(Object.getPrototypeOf(obj))
    map.set(obj, copy)
    for (const key of Object.keys(obj)) {
        copy[key] = deepClone(obj[key], map)
    }
    return copy
}

当然还有ES2021新增的structuredClone(),是原生的深拷贝

15. 数组的乱序输出

function shuffle(arr) {
    for (let i = 0; i < arr.length; i++) {
        const randomIndex = Math.round(Math.random() * (arr.length - 1 - i)) + i
        [arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]]
    }
}

16. 数组扁平化

function flatten(arr, depth = 1) {
    if (depth === 0) {
        return arr
    }
    return arr.reduce((acc, val) => {
        acc.concat(Array.isArray(val) ? flatten(val, depth - 1) : val)
    }, [])
}

还有一个取巧的办法就是toString()后再split()

17. 数组去重

不用Set

function unique(arr) {
    const map = new Map()
    const res = []
    for (const item of arr) {
        if (!map.has(item)) {
            map.set(item, true)
            res.push(item)
        }
    }
    return res
}

ES6直接用[...new Set(arr)]

18. Array.Prototype.reduce

Array.prototype.reduce = function (fn, ...init) {
    let next = init.length ? init[0] : this[0]
    for (let i = init.length ? 0 : 1; i < this.length; i++) {
        next = fn(next, this[i], i)
    }
    return next
}

场景题

1. 实现调度器

interface ITask {
    priority: 1 | 2 | 3
    action: () => Promise<any>
}

class Schedualer {
    private readonly queue: ITask[] = []
    private running: number = 0

    add(task: ITask) {
        let index = 0
        while (index < this.queue.length && task.priority >= this.queue[index].priority) {
            index++
        }
        this.queue.splice(index, 0, task)
        Promise.resolve().then(() => this.tryRunNext())
    }

    private tryRunNext() {
        while (this.running < 2 && this.queue.length > 0) {
            const task = this.queue.shift()!
            this.running++
            task.action().finally(() => {
                this.running--
                this.tryRunNext()
            })
        }
    }
}

算法题

1. 给数字添加千分符

正则表达式方法,没有考虑小数

function numberThousands(number, seperator = ',') {
    // 这个正则捕获一个数字,其数字后面要求跟着3的倍数个数字(使用?=正向肯定预查)
    // 然后再通过'$1'来引用第一个捕获组,在捕获组后面添加分隔符
    return String(number).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + seperator)
}

如果考虑小数的话

function numberThousands(number, seperator = ',') {
    const str = String(number)
    const parts = str.split('.')
    parts[0] = parts[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + seperator)
    return parts.length > 1 ? parts.join('.') : parts[0]
}
话题状态:正常
25