前端面试必刷手写题系列 [2]

799 阅读5分钟

这个系列也没啥花头,就是来整平时面试的一些手写函数,考这些简单实现的好处是能看出基本编码水平,且占用时间不长,更全面地看出你的代码实力如何。一般不会出有很多边界条件的问题,那样面试时间不够用,考察不全面。

平时被考到的 api 如果不知道或不清楚,直接问面试官就行, api 怎么用这些 Google 下谁都能马上了解的知识也看不出水平。关键是在实现过程,和你的编码状态习惯思路清晰程度等。

注意是简单实现,不是完整实现,重要的是概念清晰实现思路清晰,建议先写伪代码,再实现具体功能。

核心概念防抖节流 我这篇已经很详细了 我不赘述,想更了解细节请仔细看这篇

4. 函数防抖 (debounce)

是什么

简单来说就是把多次执行组合成一次执行

比如我们设置了一个时间间隔 5 秒,当事件触发的间隔超过 5 秒(回调)函数才会执行如果在 5 秒内,事件又被触发,则刷新这个 5 秒至少5秒后事件没被触发才执行函数

简单手写实现

实现

  1. 第 0 步,写个测试用例先,有时候你可以要求面试官给出个,来保证理解的准确性,如果他不给,你写完用例也一定要确认下是否满足要求,不要实现原本就沟通不到位的方案,这样也展现了你的沟通能力
// 用户高频率执行的函数(需要防抖的函数),也许是个异步请求列表,成本比较高不需要频繁调用
function userHighRequencyAction(e, content) {
    console.log(e, content);
}

// 给这个高频的方法,加防抖方案输出一个防抖的function
var userDebounceAction = debounce(userHighRequencyAction, 1000);

// 如何触发那个高频函数 绑定一个onmousemove事件,来模拟高频触发
document.onmousemove = function (e) {
    userDebounceAction(e, 'test debounce'); // 给防抖函数传个参
}
  1. 那我们就先写出伪代码,理清思路

基本思路就是利用 setTimeout 这个WebApi的延迟效果设置一个定时器,当没有到达延迟时间就清除定时器(clearTimeout),并建立一个新的定时器,继续等延迟时间,如此循环。

// 测试用例得
function debounce(func, wait) {
    // 声明一个定时器
    // 我们需要返回一个函数闭包,来保存定时器 timer 状态在内存中
    return function () {
        // 时间未到清除定时器
        // 并建立一个新的定时器
        // 利用 setTimeout 时间到了再执行回调
    };
}
  1. 实现主逻辑
function debounce(func, wait) {
    // 声明一个定时器
    let timer; 
    // 我们需要返回一个函数闭包,来保存定时器 timer 状态在内存中
    return function () {
        // 时间未到第二次函数调用就来了,那么清除定时器函数
        if (timer) {
            clearTimeout(timer)
        }
        // 利用 setTimeout 时间到了再执行回调
        timer = setTimeout(func, wait);
    };
}
  1. 写完整这些特殊的点,当然 2,3你可以一步到位,我分步骤是为了举例,其他复杂题最好清楚分出步骤。

待完善点:

  1. 我们需要将 this 指向正确的对象
  2. 我们函数有些默认参数需要传递,比如 JavaScript 在事件处理函数中会提供事件对象 event 也就是 (e)
function debounce(func, wait = 1000) { 
    let timer; 
    return function () { 
        // 这里利用词法作用域,保存了 this ,在闭包中传入,我猜你用过 that 这个名
        let context = this;  
        // 所以我们也要保存下 arguments
        let args = arguments;
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(function () {
            // 用 apply 显示绑定 this
            func.apply(context, args); 
            // 其实就是 context.func(args)
        }, wait);
    };
}
  1. 上面基本结束,如果面试官再给你点拓展,就临场发挥下,不要慌写出来更好,写不出拉倒,比如加个 立即执行取消功能什么的

需求: 不希望非要等到事件停止触发后才执行,我希望立刻执行函数,然后等到停止触发 n 秒后,才可以重新触发执行。

function debounce(func, wait = 1000, immediate = false) {
    var timeout, result;

    var debounced = function () {
        var context = this;
        var args = arguments;
        if (timeout) {
          clearTimeout(timeout);
        }
        // immediate 关键改动
        if (immediate) {
            // 如果已经执行过,不再执行
            var callNow = !timeout;
            timeout = setTimeout(function(){
                timeout = null;
            }, wait)
            if (callNow) { 
              result = func.apply(context, args)
            }
        } else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
        return result;
    }
    
    debounced.cancel = function() {
        clearTimeout(timeout);
        timeout = null;
    };

    return debounced;
}

// 可以这样使用
document.getElementById("button").addEventListener('click', function(){
    setUseAction.cancel();
})

此时注意一点,就是 userHighRequencyAction 函数可能是有返回值的,所以我们也要返回函数的执行结果,但是当 immediate 为 false 的时候,因为使用了 setTimeout ,我们将 func.apply(context, args) 的返回值赋给变量,最后再 return 的时候,值将会一直是 undefined,所以我们只在 immediate 为 true 的时候返回函数的执行结果

5. 函数节流 (throttle)

是什么

简单来说 就是在指定的时间间隔内只允许我们的函数执行一次

比如一个事件在被疯狂触发,本来每秒执行几百次(回调)函数,而你使用函数节流设了个时间间隔 1s那么这个函数在 1s 内只会执行一次

简单手写实现

实现

有了上面防抖的例子,节流简单说,原理类似

简单来说,当触发事件的时候,设置一个 timer, 再次触发事件的时候 timer 存在(不为null),则不执行,直到函数执行了,把timer置空并启动设置下一个定时器。这也就保证了 wait 时间内函数只会执行一次。

function throttle(func, wait = 1000) {
    let timer, context, args;
    return function() {
        context = this;
        args = arguments;
        if (!timer) {
            timer = setTimeout(function() {
                // 执行后置定时器变量为null
                timer = null;
                func.apply(context, args)
            }, wait)
            // func.apply(context, args) 想立即执行就放这
        }
    }
}

今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友 Or 搜索我的微信号infinity_9368,可以聊天说地 加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我 presious tower shock the rever monster,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧

参考