我们用两张图表示什么是节流和防抖。
由图可见,防抖的意思是,当用户在一段时间内连续频繁的试图执行一个函数的时候,只有最后一次,函数被真正的执行。节流的意思是,当用户在某一个时刻执行了一次函数的时候,在一段时间 t 内,再次执行该函数都没有作用。
下面,我们用实例,写一个防抖和节流出来。
请先利用下面的命令安装 jquery,在项目根目录下执行。
npm install jquery -save
在 body 里添加
<button id="btn1">btn1</button>
一、防抖
let id;$("#btn1").on('click',function(e){clearTimeout(id);id = setTimeout(function(){console.log('button clicked',id)},1000);})
这是一个最简单的防抖,但是,缺点是明显的,一个临时变量被放在 window 对象里面了,污染了全局变量空间。因此,我们想把这个变量写成局部的,有经验的同学一定知道,我们该用闭包了,既,使用柯理化函数的编程思想。事实上,上面的这个例子已经是一个闭包了,因为最外层的 id 接住了函数内部 setTimeout 的返回值,因此这个函数的栈内存是不会销毁的。只是,我们的优化方案需要在此基础上再加一层。
本着这个思路,我们有:
function debounce(){let id;return function(){clearTimeout(id);id = setTimeout(function(){console.log('button clicked',id)},1000);}}var fn = debounce();$("#btn1").on('click',function(e){fn();})
首先,一个全局变量 fn 接住了 debounce 的一个返回值,闭包形成,debounce 内部的 id 被保存住,因此,每次 fn 执行的时候,它所用到的 id 都是一个,这和第一个例子当中全局作用域上面的 id 是等价的,只不过,不会污染全局作用域。
防抖的原理是,如果用户频繁的点击按钮,上一次的 setTimeout 都会立刻被下一次清除,需要执行的函数始终打不出来,只有最后一次没人清除它,因此会被执行。
二、节流
下面我们讲节流,首先,我们还是利用全局作用域写一个简单的版本。
let time = new Date();$("#btn1").on('click', function (e) {let time1 = new Date();if (time1 - time > 2000) {console.log(time1 - time);time = time1;}})
同样的,这套方法污染了全局作用域,因此,我们需要写个闭包出来,保存局部变量。
function throttle() {let time = new Date();return function () {let time1 = new Date();if (time1 - time > 2000) {console.log(time1 - time);time = time1;}}}let fn = throttle();$("#btn1").on('click', function (e) {fn();})
节流的原理比防抖更简单,当函数被成功的执行过一次,本次成功执行的时间会被记录下来,那么当用户频繁点击按钮的时候,这些次记录的时间距离上次成功执行的时间太短,小于阈值,因此不被执行。
请各位同学一定要把代码搞到本地跑一遍,这样才能加深印象。