underscore源码之学习防抖
前言
**本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。**这是源码共读的第25期,链接:juejin.cn/post/708744…
了解underscore库
underscore是一个javascript的函数式编程助手库,版本1.13.7
什么是防抖\节流
防抖本质上是优化高频率代码执行的一种手段,浏览器的resize、scroll、keypress、mousemove 等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能,
- 防抖:延迟n秒后再触发事件函数,如果n秒内又触发了该事件,则重新计时n秒
- 节流:n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
手写防抖函数
- 简单版本
- 频繁的调用debounce函数,就清空定时器,重新计时执行
function debounce(fun, wait) {
let timeout = null;
return function () {
if(timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
fun.apply(this, arguments);
}, wait);
}
}
- 进阶版本,增加immediate
function debounced(func, wait, immediate) {
let timeout = null
return function(...args) {
// 已经注入了定时器,则清空
if (timeout) clearTimeout(timeout)
// 立即执行逻辑
if (immediate) {
let callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
// 第一次调用立即执行func, 后面要等timeout = null执行之后才会调用func函数
if (callNow) func.apply(this, args)
} else {
timeout = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}
}
debounce.js源码解读
- later函数的时间计算更加严谨
import restArguments from './restArguments.js';
import now from './now.js';
// 导出debounce函数,debounce函数里有三个形参,fun<需要防抖的函数>、wait<等待的时间,毫秒单位>、
// immediate<是否立即执行>
export default function debounce(func, wait, immediate) {
var timeout, previous, args, result, context;
var later = function() {
// 计算调用debounced函数的时候至目前later调用已经过了多少时间,
var passed = now() - previous;
if (wait > passed) {
// 如果当前等待时间 < wait, 则继续注入setTimeout等待wait - passed时间后再执行
timeout = setTimeout(later, wait - passed);
} else {
// 清空timeout, 防止注入了多的setTimeout
timeout = null;
// 判断不是immediate,执行func并缓存result, 这里指的是只要在wait时间内且immediate为true的情况下调用过func, 被防抖函数fun都不会再次执行,
if (!immediate) result = func.apply(context, args);
// timeout不为空的情况下,设置args和context为null
if (!timeout) args = context = null;
}
};
var debounced = restArguments(function(_args) {
context = this;
args = _args;
previous = now();
if (!timeout) {
// 注入一个setTimeout函数,等待wait时间后执行later函数
timeout = setTimeout(later, wait);
// 如果存在传参immediate为true, 则立即调用func函数并记录返回结果
if (immediate) result = func.apply(context, args);
}
return result;
});
// debounced函数增加cancel属性函数来取消函数的防抖效果
debounced.cancel = function() {
clearTimeout(timeout);
timeout = args = context = null;
};
return debounced;
}
- debounced还引用了restArgements是一个函数
- 这个函数
restArguments的作用是将一个函数的参数分为固定参数和剩余参数,并将剩余参数打包成一个数组传递给原函数。它的功能类似于 ES6 中的 Rest Parameters(剩余参数)语法(...rest),但在 ES6 之前,JavaScript 并没有原生支持剩余参数,因此需要手动实现
export default function restArguments(func, startIndex) {
// 如果没有startIndex, 则将函数的最后一个参数作为剩余参数
startIndex = startIndex == null ? func.length - 1 : +startIndex;
return function() {
var length = Math.max(arguments.length - startIndex, 0),
rest = Array(length), // 声明存储剩余参数的数组
index = 0;
for (; index < length; index++) {
rest[index] = arguments[index + startIndex]; // 将剩余参数存在rest数组中
}
switch (startIndex) {
case 0: return func.call(this, rest);
case 1: return func.call(this, arguments[0], rest);
case 2: return func.call(this, arguments[0], arguments[1], rest);
}
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
args[startIndex] = rest;
// apply的传参需要时数组,将所有参数整合到数组args中
return func.apply(this, args);
};
}
结语
多积累和学习总有收获,如有问题,欢迎指出,谢谢