js常见手写题

58 阅读2分钟

1. 手写instanceof

instanceof判断一个实例是否是其父类或者祖先类型的实例

let myinstanceof = function (target, origin) {
    while(target) {
        if (target.__proto__ === origin.prototype) {
            return true
        }
        target = target.__proto__
    }
    return false
}

2. 实现数组的map方法

  • 数组map返回一个新数组,新数组的每个元素对应源数组中的对应位置元素调用一次提供的函数后的返回值
  • map方法有两个参数,一个是操作数组元素的方法fn,一个是this指向
  • fn有三个参数,分别是当前数组项,当前索引,当前数组
Array.prototype.myMap = function (fn, thisValue) {
    let res = []
    thisValue = thisValue || []
    let arr = this
    for (let i = 0; i < arr.length; i++) {
        res.push(fn.call(thisValue, arr[i], i, arr))
    }
    return res
}

3. 数组扁平化

// 使用es6的flat(depth)
// 利用cconcat
function flatten(arr) {
    var res = [];
    for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            res = res.concat(flatten(arr[i]))
        } else {
            res.push(arr[i])
        }
    }
    return res
}

4. 函数柯里化

function curry(fn, args) {
    let length = fn.length;
    args = args || []
    return function() {
        let subArgs = args.slice(0);
        subArgs = subArgs.concat(...arguments);
        if (subArgs.length >= length) {
            fn.apply(this, subArgs)
        } else {
            return curry.call(this, fn, subArgs)
        }
    }
}

5. 深拷贝

// 深拷贝
function deepCopy(newObj, oldObj) {
    for (let k in oldObj) {
        let item = oldObj[k]
        if (item instanceof Array) {
            newObj[k] = []
            deepCopy(newObj[k], item)
        } else if (item instanceof Object) {
            newObj[k] = {}
            deepCopy(newObj[k], item)
        } else {
            newObj[k] = item
        }
    }
    return newObj
} 

6. 手写call、apply、bind


// 手写call
Function.prototype.myCall = function(content) {
    const obj = content || window
    obj.fn = this
    const arg = [...arguments].slice(1)
    let res = obj.fn(...arg)
    delete obj.fn
    return res
}

// 手写apply
Function.prototype.myApply = function(content) {
    const obj = content || window
    obj.fn = this
    const arg = arguments[1] || []
    let res = obj.fn(...arg)
    delete obj.fn
    return res
}

// 手写bind
Function.prototype.myBind = function (content) {
    var args = [...arguments].slice(1),
    fn = this
    return function Fn() {
        return fn.apply(this instanceof Fn ? this : content, args.content(...arguments))
    }
}

7. 手写new

  • 创建一个新对象obj
  • 将空对象的原型__proto__指向构造函数的prototype
  • 使用apply改变this指向
  • 如果无返回值或者返回一个非对象值,则将obj返回作为新对象,如果返回值是一个新对象那么直接返回该对象
function mynew (Fn, ...args) {
    let obj = {}
    obj.__proto__ == Fn.prototype
    let result = Fn.apply(obj, args)
    return result instanceof Object ? result : obj
}


8. 手写eventEmit

class EventBus {
    constructor() {
        this.eventList = {} // 创建对象收集事件
    }

    // 发布事件
    $on(eventName, fn) {
        this.eventList[eventName] ? this.eventList[eventName].push(fn) : this.eventList[eventName] = [fn]
    }

    // 订阅事件
    $emit(eventName) {
        if (!eventName) throw new Error('请传入事件名')
        const data = [...arguments].slice(1)
        if (this.eventList[eventName]) {
            this.eventList[eventName].forEach(item => {
                item(...data)
            })
        }
    }

    // 执行一次
    $once(eventName, fn) {
        const _this = this
        function oncehandle() {
            fn.apply(null, arguments)
            _this.$off(eventName, oncehandle) // 执行成功后取消监听
        }
        this.$on(eventName, oncehandle)
    } 

    $off(eventName, fn) {
        // 不传入参数时,全部取消订阅
        if (!arguments.length) {
            return (this.eventList = {})
        }
        if (arguments.length === 1 || !fn) {
            return (this.eventList[eventName] = [])
        }
        this.eventList[eventName] = this.eventList[eventName].filter((f) => f !== fn)
    }   
}