手撕常见面试中的js函数

148 阅读2分钟

面试中经常会遇到面试官让手撕函数,我最近准备春招所以想总结一下,一部分根据自己理解实现,一部分借鉴,若有错误请指正。话不多说,上代码吧❤

1.函数柯里化

/**
 * 函数柯里化
 * @param {Function} fn 
 * @param  {...any} args 
 * @returns 
 */
function curry(fn, ...args) {
    return function (...addArg) {
        let totalArgs = [...args, ...addArg]
        if (totalArgs.length >= fn.length) {
            return fn.apply(this, totalArgs)
        } else {
            return curry.call(this, fn, ...totalArgs)
        }
    }
}
//test
let curryTest = curry(test)
console.log(curryTest(1, 2)(3, 4)(5, 6, 7))
console.log(curryTest(1, 2)(3, 4)(5, 6, 7)(8)(9))

2.实现Map

/**
 * 实现Array.prototype.map
 * @param {function} fn 
 * @param {Object} thisArg 
 * @returns 
 */
Array.prototype.map = function (fn, thisArg) {
    // 处理数组类型异常
    if (this === null || this === undefined) {
        throw new TypeError('this is not a array')
    }
    //处理函数异常
    if (!fn instanceof Function) {
        throw new Error(fn + 'is not a function')
    }
    let array = Object(this)//V8当中介绍先转换为对象我就这样写了
    let len = array.length
    let result = new Array(len)
    for (let j = 0; j < len; j++) {
        if (j in array) {
            let value = array[j]
            result[j] = fn.call(thisArg, value, j, array)
        }
    }
    return result
}
function mapTest(value, ...args) {
    return value * 2
}
let arr = [1, 2, 3, 4, 5]
console.log(arr.map(mapTest, undefined))

3.实现Reduce

/**
 * 实现Array.prototype.reduce
 * @param {function} fn 
 * @param {Number} initNum 
 * @returns
 */
function myReduce(fn, initNum) {
    if (this === null || this === undefined) {
        throw new TypeError('this is not a array')
    }
    if (!fn instanceof Function) {
        throw new Error(fn + 'is not a function')
    }
    let array = Object(this)
    let len = array.length
    let i = 0
    //这里处理当没有设置初始值的时候
    if (initNum) {
        for (; i < len; i++) {
            if (array[i]) {
                initNum = array[i]
                break
            }
        }
    }
    //如果数组为空
    if (i == 0 || initNum) {
        throw new Error('this is a null Array')
    }
    i = 0
    let sum = initNum
    for (; i < len; i++) {
        if (i in array) {
            sum = fn.call(undefined, sum, array[i], i, array)
        }
    }
    return sum
}
let arr = [1, 2, 3, 4, 5]
function testReduce(sum, value) {
    return sum + value
}
console.log(arr.reduce(testReduce))

4.实现Call

/**
 * 实现call
 * @param {Object} thisArg
 * @param {Function} fn
 * @param {...any} args
 */
function call(context, ...args) {
    context = context || window //如果传入null or undefined,默认绑定Window
    let fn = Symbol('fn')//创建一个唯一值
    context.fn = this
    let result = eval('context.fn(...args)')//call传入的是参数列表
    delete context.fn
    return result
}

5.实现apply

/**
 * 实现apply
 * @param {Object} thisArg
 * @param {Function} fn
 * @param {...any} args
 */
function apply(context, ...args) {
    context = context || window
    let fn = Symbol('fn')
    context.fn = this
    let result = eval('context.fn(args)')//apply传入的是参数数组
    delete context.fn
    return result
}

6.实现bind

/**
 * 实现bind
 * @param {Object} context 
 * @param  {...any} args 
 */
Function.prototype.bind = function (context, ...args) {
    //处理函数异常
    if (!this instanceof Function) {
        throw new Error('this is not a function,cant use bind')
    }
    let _this = this//保存当前this
    let bindFun = function () {//返回绑定传入context的函数
        _this.apply(context, args.concat(Array.from(arguments)))//支持柯里化传值
    }
    bindFun.prototype = Object.create(_this.prototype)
    return bindFun
}

7.Promise

(1)Promise.resolve

/**
 * 实现Promise.resolve
 * @param {Promise} promise
 * @returns resolve()
 */
Promise.resolve = function (params) {
    //传入的并非一个promise,直接返回
    if (!params instanceof Promise) {
        return params
    }
    return new Promise((resolve, reject) => {
        //传入的是一个thenable,将其状态作为最终状态返回
        if (params && params.then && typeof params === 'function') {
            params.then(resolve, reject)
        } else {
        //其他情况
            resolve(params)
        }
    })

}

(2)Promise.all

/**
 * 实现Promise.all
 * @param {Promise} promises
 * @returns resolve(array)
 */
Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let len = promises.length
        let result = []
        //传入的数组为空
        if (len == 0) {
            resolve(result)
            return
        }
        let count = 0
        for (let i = 0; i < len; i++) {
            //Promise.resolve(promises[i]):可能存在某一个不是promise
            Promise.resolve(promises[i]).then(res => {
                result.push(res)
                count++
                //全部执行成功,返回数组
                if (count == len) {
                    return resolve(result)
                }
            }).catch(err => {
                //出现一个失败直接reject
                reject(err)
                return
            })
        }
    })
}

(3)Promise.race

/**
 * 实现Promise.race
 * @param {Promise} promises
 * 
 */
Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        let len = promises.length
        if (len == 0) {
            return
        }
        //与all相反,仅一次成功或失败就结束
        for (let i = 0; i < len; i++) {
            Promise.resolve(promises[i]).then(res => {
                resolve(res)
                return
            }).catch(err => {
                reject(err)
                return
            })
        }
    })
}

8.防抖

/**
 * 防抖:指触发事件后在 n秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
 * @param {Function} fn 
 * @param {Number} delay 
 * @returns 
 */
function debounce(fn,delay){
    let timer = null
    return function(...args){
        /* 进入该分支语句,
        说明当前正在一个计时过程中,
        并且又触发了相同事件。
        所以要取消当前的计时,
        重新开始计时*/
        if(timer){
            clearTimeout(timer)
        }
        //说明当前并没有在计时,那么就开始一个计时
        timer = setTimeout(()=>{
            fn.apply(this,args)
        },delay)
    }
}

9.节流

/**
 * 节流:指连续触发事件但是在 n秒中只执行一次函数
 * @param {Function} fn 
 * @param {Number} delay 
 * @returns 
 */
function throttle(fn,delay){
    let start = 0
    return function(...args){
        let end = Date.now()
        if(end-start >= delay){
            fn.apply(this,args)
            start = end
        }
    }
}

后续会继续更新,有错请指教,别骂我就行,我会哭哈哈哈哈❤