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

简介: JS手写 call、防抖节流

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 = debounce(function () {
    console.log('debounce'); // 持续滚动只会间隔1s有节奏地执行打印
}, 1000)

定时器 debounce 实现:

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

时间戳 debounce 实现:

function debounce(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 = throttle(function () {
    console.log('throttle'); // 直到完全停止滚动后1s才执行输出
}, 1000)

定时器实现:

function throttle(fn, delay = 1000) {
    let timer = null
    return function () {
        if (timer) {
            clearTimeout(timer) // 与防抖的主要区别,前面的任务清除,只保留最后一次执行
        }
        timer = setTimeout(() => {
            fn.call(this, ...arguments)
            timer = null
        }, delay);
    }
}
相关文章
|
20天前
|
JavaScript
变量和函数提升(js的问题)
变量和函数提升(js的问题)
|
20天前
|
JavaScript
常见函数的4种类型(js的问题)
常见函数的4种类型(js的问题)
11 0
|
20天前
|
JavaScript
写一个函数将N组<>(包含开始和结束),进行组合,并输出组合结果 (js)
写一个函数将N组<>(包含开始和结束),进行组合,并输出组合结果 (js)
9 0
|
1月前
|
自然语言处理 JavaScript 网络架构
js开发:请解释什么是ES6的箭头函数,以及它与传统函数的区别。
ES6的箭头函数以`=&gt;`定义,简化了函数写法,具有简洁语法和词法作用域的`this`。它无`arguments`对象,不能用作构造函数,不支持`Generator`,且不改变`this`、`super`、`new.target`绑定。适用于简短表达式,常用于异步编程和高阶函数。
17 5
|
6天前
|
缓存 JavaScript 前端开发
js的入口函数,入口函数的作用
js的入口函数,入口函数的作用
12 4
|
21天前
|
存储 前端开发 JavaScript
JavaScript如何封装一些常见的函数来提高工作效率
通过封装这些常见函数,你可以在项目中重复使用,提高代码的复用性和工作效率。这些函数包括获取元素、发起Ajax请求、处理本地存储、日期格式化、定时器等功能,可以在不同场景中减少重复的代码编写。
7 0
JavaScript如何封装一些常见的函数来提高工作效率
|
22天前
|
JavaScript 前端开发
JavaScript中call()与apply()的作用与区别?
JavaScript中call()与apply()的作用与区别?
21 2
|
30天前
|
JavaScript
js开发:请解释什么是ES6的Generator函数,以及它的用途。
ES6的Generator函数是暂停恢复的特殊函数,用yield返回多个值,适用于异步编程和流处理,解决了回调地狱问题。
16 6
|
1月前
|
JSON JavaScript 前端开发
JavaScript随手笔记---数组中相同的元素进行分组(数据聚合) groupBy函数
JavaScript随手笔记---数组中相同的元素进行分组(数据聚合) groupBy函数
|
1月前
|
JavaScript
JS中call()、apply()、bind()改变this指向的原理
JS中call()、apply()、bind()改变this指向的原理