阅读 110

一看就懂的JS手写函数(call、防抖节流)

这是我参与更文挑战的第11天,活动详情查看 更文挑战

call函数

先从改变this指向上简单实现一个方法添加到Function的原型链上:

Function.prototype.myCall = function (content) {
    content.fn = this
    const result = eval(`content.fn()`)
    return result
}
复制代码

这就实现了call函数核心部分,因为使用了字符串的形式,所以函数的参数部分还需要进行特殊处理:

Function.prototype.myCall = function (content) {
    content.fn = this
    /** 处理参数 */
    const args = []
    for (let i = 1; i < arguments.length; i++) {
        args.push(`arguments[${i}]`) // 直接push会导致强转字符串时出现:[]
    }
    /** */
    const result = eval(`content.fn(${args})`)
    return result
}
复制代码

基本可以了,但还有问题,临时属性的处理,最终优化结果:

Function.prototype.myCall = function (content) {
    const fn = `fn_${(Math.random()*999).toFixed()}` // 防止极端情况下属性名冲突
    content[fn] = this
    const args = []
    for (let i = 1; i < arguments.length; i++) {
        args.push(`arguments[${i}]`)
    }
    const result = eval(`content[fn](${args})`)
    delete content[fn] // 使用后释放
    return result
}
复制代码

写个例子测试下:

const a = {
    name: 'a',
    say: function (t) { console.log(`${t}, ${this.name}`) }
}
const b = { name: 'b' }

a.say.call(b, 'hi') // hi, b
a.say.myCall(b, 'hello') // hello, b
复制代码

防抖

以滚动事件为例,防抖即是滚动过程中低频率地触发事件

window.onscroll = throttle(function () {
    console.log('throttle'); // 持续滚动只会间隔1s有节奏地执行打印
}, 1000)
复制代码

定时器throttle实现:

function throttle(fn, delay = 1000) {
    let timer = null;
    return function () {
        if (timer) {
            return
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments);
            timer = null; // 闭包,释放timer
        }, delay)
    }
}
复制代码

时间戳throttle实现:

function throttle(fn, delayTime = 1000) {
    let lastTime = 0
    return function () {
        const nowTime = new Date().getTime()
        if (nowTime - lastTime > delayTime) {
            fn.call(this, ...arguments)
            lastTime = nowTime // 也是闭包,维护一个时间戳
        }
    }
}
复制代码

节流

还是以滚动事件为例,节流即是滚动过程中只触发一次事件

window.onscroll = debounce(function () {
    console.log('debounce'); // 直到完全停止滚动后1s才执行输出
}, 1000)
复制代码

定时器debounce实现,与防抖就是一个模子刻出来的,不同的是节流不会多次执行:

function debounce(fn, delay = 1000) {
    let timer = null
    return function () {
        if (timer) {
            clearTimeout(timer) // 与防抖的主要区别,前面的任务清除,只保留最后一次执行
        }
        timer = setTimeout(() => {
            fn.call(this, ...arguments)
            timer = null
        }, delay);
    }
}
复制代码
文章分类
前端
文章标签