那些手写题的细节你注意到了么(一)

最近也是在准备面试,对于我自己来说过不去的坎除了算法就是手写题了,认认真真的准备了,那么下面这些细节你注意到了么~

1.防抖

1.1 防抖怎么写

防抖是啥?就是让一个函数一段时间内只执行一次,最常见的案例就是表单的提交按钮以及页面大小的调整,这里我打断一下,贴一下,我找到的关于 event 对象的几个坐标属性的图,细节一波—> 传送门

16b91a360f58be3f_tplv-t2oaga2asx-watermark.webp

回归正题,一段时间执行一次,一听咱就反应过来了,这要用定时器啊,是的,大佬们是这么写的。

    <button id="button">按钮</button>
    <script>
        const button = document.getElementById('button')
        function debonce(fn,time){
            let timer
            return function(){
                clearTimeout(timer)
                timer = setTimeout(() => {
                    fn.apply(this,arguments)
                    console.log('测试',arguments)
                },time)
            }
        }
        button.addEventListener('click',debonce(function(event){console.log('我来了',event,this)},2000))event
    </script>
复制代码

1.2 理解防抖

我们给一个按钮写了一个防抖,监听它的点击事件,写了一个 debonce 函数,这个函数返回一个匿名函数,DOMaddEventListener 的第二个参数应该是一个函数名,点击事件发生的时候 DOM 会帮我们调用这个函数,上边在 addEventListener 里面我们的 debonce 函数带了括号参数,所以点击事件发生后,真正执行的是 debonce 里面的匿名函数。这种写法我理解的没错的话,就是柯里化。通过在函数内返回一个函数,来连续调用,连续获得参数。

通过柯里化,我们能够在匿名函数内拿到 funtime 写一个定时器,将 fun 在定时器内执行,同时在每次定时器启动时,先清理定时器,我们传的是2秒,这就保证 fn 函数执行的最短间隔是2秒,你如果一直疯狂点,只要间隔事件时间没有超过两秒,最终也只会执行最后一次。,那第一遍调用都没有定时器,清理啥呢?所以我们在匿名函数外,定义一个 time 利用闭包,即使 debonce 函数执行完了,匿名函数用到了它,就不会清除。以上就是对于防抖的普遍认知。下面再写一些细节

1.3 防抖细节

1.3.1 this

this,说到底,最终起作用的函数,还是 fn ,我们要保证 fn 里面的 this 是正常的,怎样才是正常的呢,我觉得三句话可以概括

  1. 谁调用,this 就指向谁,普通函数中的 this 指向 window,严格模式下是 undefined
  2. 构造函数用到了 new 操作符,函数中的 this,指向新生成的对象
  3. 箭头函数中的 this 是外层接触到的第一个 this

如果不用 apply 执行的时候 fn 里面的 this 是指向 window 的,正常情况下要指向button ,所以我们使用 apply 改变 fn 中的 this 指向。

  • apply call bind 都是定义在原型上的,能够改变函数的 this 指向,第一个参数就是你想把 this 指向哪,后面的参数,是传给调用函数的,一些区别就是传参的不同,apply 将所有要传的参数,放进一个数组,callbind 则是该怎么传,还有一个区别是,调用 call,apply 后函数是理解执行的, bind 则是返回一个新的函数。
fun1.call(bb,11,22);
fun1.apply(bb,[11,22]);
fun1.bind(bb,11,22)()
复制代码

1.3.2 arguments

arguments 是一个类数组,instanceof 一下 你会发现,它不是对象是数组,但它又有 length 属性,能够通过下标拿值。通过 arguments 能够拿到 实参

        function app(){
            console.log(arguments)
        }
        app(1,2,3,4)
复制代码

image-20211121155729878.png

另外,和 this 一样,箭头函数里面的 auguments 也是外层接触到的第一个的

        function app(){
            const fn = (a,b,c,d) => {
                console.log('我是直接打印参数',a,b,c,d)
                console.log('我是arguments对象',arguments)
            }
            fn(5,6,7,8)
        }
        app(1,2,3,4,5)
复制代码

image-20211121160751860.png

所以这个 arguments 的作用是啥呢?开头这个例子,点击按钮后,因为监听了,它调用我们的函数的时候,是会把事件对象给到我们,但给到的是匿名函数,真正要用的是 fn 所以就用到了 argument.说到底,就是帮助我们拿到执行函数的参数,我把事件监听改成自己调用,你可能就明白了

    debonce(function(params){console.log('我来了',params),console.log('我是this',this)},2000)('你好啊')
复制代码

怎么样?应该够细吧,哈哈哈哈~

2. 节流

节流,说实话和防抖挺像的,来看一个实际案例,监听滑轮滚动

    <div style="background-color: red;width:30px;height:2000px"></div>
    <script>
        const fn = function(){
            console.log('我触发执行了')
        }
        document.addEventListener('scroll',fn)
    </script>
复制代码

image-20211121165414418.png

我不想让它触发这些这么多次,而是3秒里面只执行一次,要怎么写呢,看到了两种,一种是利用时间戳,一种还是用定时器。

    <div style="background-color: red;width:30px;height:2000px"></div>
    <script>
        const fn = function(event){
            console.log('我触发执行了',event)
        }
        const throttle = function(fn,wait){
            let time1 = 0
            return function(){
                let time2 = new Date()
                if(time2-time1 >wait){
                    fn.apply(this,arguments)
                    time1 = time2
                }
            } 
        }
        document.addEventListener('scroll',throttle(fn,1000))
    </script>
复制代码

我自己感觉,从目的上来说,我感觉不到防抖和节流有很大的区别,但是从代码上说的话,防抖没超过时间间隔,会清理定时器,所以执行的都是最新的函数,节流的的话,没超过时间间隔,就不执行,要等到前一个函数执行了,才能执行下一个。

后面还会写其他手写题,写这种博客收益最大的就是自己了,可能对于新手帮助会大一些,对于大佬们来说,就有点过家家了,见谅,精神食粮,欢迎食用!

猜你喜欢

转载自juejin.im/post/7032964004507025416