一、使用场景
- 函数内执行耗时操作
- 函数执行不频繁,只有最后一次有意义
- 举例:鼠标移动事件,键盘输入事件…等需要逻辑处理时
二、实现思路(详解,es6之前版本)
举例场景为键盘输入事件,函数内部通过apply改变this指向,通过slice处理arguments(参数集)伪数组
- 前置
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>函数防抖</title>
</head>
<body>
<input type="text">
</body>
<script>
var input = document.querySelector('input');
input.addEventListener('input', function(e) {
console.log(e.target.value);
});
</script>
</html>
- 实现延时触发效果 – 使用高阶函数思想封装函数
高阶函数(至少满足以下条件之一)
:1.接受一个或多个函数作为参数。2.返回一个新的函数。
function debounce(fn, delay) {
return function() {
setTimeout(() => {
fn();
}, delay);
}
}
var newFn = debounce(function() {
console.log(111);
}, 1000);
var input = document.querySelector('input');
input.addEventListener('input', newFn);
- 实现只响应最后一次触发 – 利用setTimeout返回值进行处理
这里将this改为打印内容,我们可以发现this为undefined。
原因:函数的this指向调用者,那么当前fn是在setTimeout中调用,指向全局
function debounce(fn, delay) {
var timer = null;
return function() {
clearTimeout(timer);
timer = setTimeout(() => {
fn();
}, delay);
}
}
var newFn = debounce(function() {
console.log(this);
}, 1000);
var input = document.querySelector('input');
input.addEventListener('input', newFn);
- 实现this绑定和参数传递
arguments:函数调用时传入的所有参数(伪数组)
function debounce(fn, delay) {
var timer = null;
return function() {
clearTimeout(timer);
timer = setTimeout(() => {
var args = Array.prototype.slice.call(arguments,0); // 伪数组转化
fn.apply(this,args);
}, delay);
}
}
var newFn = debounce(function(e) {
console.log(this.value,e);
}, 1000);
var input = document.querySelector('input');
input.addEventListener('input', newFn);
- 自定义参数传递
function debounce(fn, delay) {
var timer = null;
return function() {
clearTimeout(timer);
timer = setTimeout(() => {
var args = Array.prototype.slice.call(arguments,0);
fn.apply(this,args);
}, delay);
}
}
var newFn = debounce(function(e,a) {
console.log(e,a);
}, 1000);
var input = document.querySelector('input');
input.addEventListener('input', (e)=>{
newFn(e,1);
});
三、es6实现
使用箭头函数、解构赋值、剩余参数等现代 JavaScript 特性
!!!箭头函数没有自己的 this,它会继承定义时所在作用域的 this
const debounce = (fn, delay) => {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
}
}
const newFn = debounce(function(e) {
console.log(e, this.value);
}, 1000);
const input = document.querySelector('input');
input.addEventListener('input', newFn);