手写代码

156 阅读2分钟

call、apply和bind

call

思想:

  • 使用对象调用函数,this指向该对象
    • 将要调用call的函数放到传入的对象上
    • 使用对象调用函数
    • 删除掉该属性
Function.prototype.call2 = function (context) {
    if (typeof this !== 'function') {
        throw new TypeError('error')
    }
    
    // self表示传入的this
    const self = [null, undefined].includes(context) ? Window : Object(context)
    // args表示传入的参数
    const args = [...arguments].slice(1)
    
    // fn表示对象的属性,防止传入的this里就有fn属性,使用Symbol定义
    const fn = Symbol('fn')
    self[fn] = this
    
    // 执行函数,result表示函数返回值
    const result = self[fn](...args)
    // 删除掉该属性
    delete self[fn]
    return result
}

apply

  • 与call的思想相似
Function.prototype.apply2 = function (context) {
    if (typeof this !== 'function') {
        throw new TypeError('error')
    }
    
    // self表示传入的this
    const self = [null, undefined].includes(context) ? Window : Object(context)
    
    // fn表示对象的属性,防止传入的this里就有fn属性,使用Symbol定义
    const fn = Symbol('fn')
    self[fn] = this
    
    // 根据是否传入的参数执行函数
    const result = Array.isArray(arguments[1]) ? self[fn](...arguments[1]) : self[fn]()
    
    delete self[fn]
    
    return result
}

bind

  • 注意考虑bind后的函数,可以用作构造函数的情况
Function.prototype.bind2 = function (context) {
    if (typeof this !== 'function') {
        throw new TypeError('error')
    }
    
    // self表示原函数
    const self = this
    // args表示bind时传入的参数
    const args = [...argument].slice(1)
    
    return function F() {
        if (this instanceof F) {
            // bind后的函数作为构造函数调用
            return new self(...args, ...arguments)
        }
        return self.apply(context, args.contact([...arguments]))
    }
}

函数防抖和节流

函数防抖

  • 函数防抖指的是事件触发n秒后执行。若事件触发的n秒内又触发,则重新计时
function debounce(func, wait, immediate) {
    let timer
    return function () {
        timer && clearTimeout(timer)
        if (immediate) {
            // immediate为真,表示事件触发立即执行,然后再n秒计时
            const isCallNow = !timer
            if (isCallNow) {
                func.apply(this, arguments)
            }
            timer = setTimeout(() => {
                timer = null
            }, wait)
        } else {
            timer = setTimeout(() => {
                func.apply(this, arguments)
            }, wait)
        }
    }
}

函数节流

  • 函数节流指的是事件频繁触发,每隔n秒执行一次
// 使用时间戳实现函数节流
function throttle (func, wait) {
    let pre = 0
    return function () {
        const now = +new Date()
        if (now - pre > wait) {
            func.apply(this, arguments)
            pre = now
        }
    }
}
// 使用计时器实现函数节流
function throttle (func, wait) {
    let timer
    return function () {
        if (!timer) {
            timer = setTimeout(() => {
                timer = null
                func.apply(this, arguments)
            }, wait)
        }
    }
}

发布-订阅模式的事件类

class Event {
    constructor() {
        this._cache = new Map()
    }

    // 注册事件
    on(type, cb) {
        !this._cache.has(type) && this._cache.set(type, new Set())

        !this._cache.get(type).has(cb) && this._cache.get(type).add(cb)

        return this
    }

    // 触发事件
    trigger(type, ...data) {
        if (this._cache.has(type) && this._cache.get(type).size > 0) {
            for (const cb of this._cache.get(type)) {
                cb(...data)
            }
        }
        return this
    }

    // 移除事件
    off(type, cb) {
        if (this._cache.has(type)) {
            if (cb) {
                const cbs = this._cache.get(type)
                for (const callback of cbs) {
                    if (callback === cb || callback.cb === cb) {
                        cbs.delete(callback)
                    }
                }
            } else {
                this._cache.delete(type)
            }
        }
        return this
    }

    // 只监听一次
    once(type, cb) {
        const on = (...args) => {
            this.off(type, cb)
            cb.apply(this, args)
        }
        on.cb = cb
        this.on(type, on)
        return this
    }
}

未完待续。。。