学习链接
一、函数节流(throttle)
函数节流:一个函数执行一次后,只有大于设定的执行周期后才会执行第二次。
有个需要频繁触发函数,出于优化性能角度,在规定时间内,只让函数触发的第一次生效,后面不生效。
版本一
function throttle(func, delay = 500) {// 记录上一次函数触发的时间let lastTime = 0; // 设为0 使得第一次可直接触发return function(...args) {// 记录当前函数触发的时间let nowTime = Date.now();if (nowTime - lastTime > delay) {// 修正this指向问题func.apply(this, args);// 同步时间lastTime = nowTime;}}}
测试
html,body {/* 使得滚动条出现 */height: 500%;}
function throttle(func, delay = 500) {// 记录上一次函数触发的时间let lastTime = 0; // 设为0 使得第一次可直接触发return function(...args) {// 记录当前函数触发的时间let nowTime = Date.now();if (nowTime - lastTime > delay) {// 修正this指向问题func.apply(this, args);// 同步时间lastTime = nowTime;}}}document.onscroll = throttle(function() {console.log('scroll事件被触发了' + Date.now());}, 1500);
版本二
// options.leading 来表示是否可以立即执行一次,// opitons.trailing 表示结束调用的时候是否还要执行一次,默认都是 true。// 不能同时将 leading 或 trailing 设置为 false 否则不会执行function throttle(func, delay, options) {let timer, context, args;let lastTime = 0;if (!options) options = {};const later = function() {lastTime = options.leading === false ? 0 : new Date().getTime();timer = null;func.apply(context, args);};const throttled = function(...args) {const nowTime = new Date().getTime();if (!lastTime && options.leading === false) { // 用===false 使得默认情况为truelastTime = nowTime; // 开始时不调用}const remaining = delay - (nowTime - lastTime); // 本轮节流剩余时间context = this; // later中使用// <=0: 间隔已经超过节流时间 可执行// >delay: 执行later后更新lastTime可能导致的情况 即重新启动计时执行第一次if (remaining <= 0 || remaining > delay) {if (timer) {clearTimeout(timer); // 未结束调用 清空timertimer = null;}lastTime = nowTime; // 更新上一次的时间func.apply(context, args);} else if (!timer && options.trailing !== false) { // 用!==false 使得默认情况为truetimer = setTimeout(later, remaining); // 结束时调用}};return throttled;}document.onscroll = throttle(function() {console.log('scroll事件被触发了' + Date.now());}, 1500);
二、函数防抖(debounce)
防抖函数:一个需要频繁触发的函数,在规定时间内,只让最后一次生效,前面的不生效。
版本一
function debounce(func, delay = 300) {// 存储计时器let timer = null;return function(...args) {// 清除计时器clearTimeout(timer);// 重新计时timer = setTimeout(() => {func.apply(this, args);}, delay);}}
测试
<button id="btn">按钮</button><script type="text/javascript">function debounce(func, delay = 300) {// 存储计时器let timer = null;return function(...args) {// 清除计时器clearTimeout(timer);// 重新计时timer = setTimeout(() => {console.log('this: ', this);func.apply(this, args);}, delay);// 或者// // 固定this指向// const context = this;// // 重新计时// timer = setTimeout(function() {// console.log('this: ', this);// console.log('context: ', context);// func.apply(context, args);// }, delay);}}document.getElementById('btn').onclick = debounce(function() {console.log('点击事件被触发' + Date.now())}, 1000)</script>
版本二
immediate 是否可立即执行一次。
<button id="btn">按钮</button><button id="cancelBtn">取消</button><script type="text/javascript">function debounce(func, wait = 500, immediate = false) {let timeout = null, result = null;const debounced = function (...args) {// 固定this指向let context = this;// 清除计时器clearTimeout(timeout);if (immediate) { // 是否立即执行// 如果已经执行过,不再执行let callNow = !timeout;timeout = setTimeout(function(){timeout = null;}, wait)// 第一次立即执行if(callNow) result = func.apply(context, args);} else {timeout = setTimeout(function(){result = func.apply(context, args)}, wait);}return result;};debounced.cancel = function() {clearTimeout(timeout);timeout = null;};return debounced;}const setUseAction = debounce(function() {console.log('点击事件被触发' + Date.now())}, 1000, true);// 使用防抖document.getElementById('btn').onclick = setUseAction;// 取消定时器document.getElementById('cancelBtn').onclick = setUseAction.cancel;</script>
