一、一种现象
有一个input框,每次输入都要触发一次change事件,其实这大可不必,比如我输入 “上海” 两个字,竟然触发了9次。
<body>
<input type="text" />
<script src="./t.js"></script>
</body>
const input = document.querySelector('input')
input.oninput = function() {
console.log(this.value)
}
假如每次change都会和后端发生一次交互,这个性能就会很惨。
怎么才能让这个次数变的少?
二、防抖
用户触发事件过于频繁,只要最后一次事件的操作。比如我们可以设置500ms,只要在500ms内你一直还在输入,也就是说你的停顿是少于500ms的,那么就不触发change事件里的复杂逻辑。在执行复杂逻辑之前就把定时器清理掉。
const input = document.querySelector('input')
let t = null
input.oninput = function() {
if (t !== null) {
clearTimeout(t)
}
t = setTimeout(() => {
// 用一句打印代替复杂的业务逻辑
console.log(this.value)
}, 500)
}
三、代码改进
上面的代码有2个大毛病:
- let t是个全局变量;
- 防抖的逻辑 if (t !== null) 和 业务逻辑都混在一起了。
用闭包来改进这段代码
上面,在调用debounce的时候,第一个参数是传递一个函数,
我们传的是一个() => {console.log(this.value)} ,这里的this是window,但是我们想要的是input。
所以,可以用call的方式来
const input = document.querySelector('input')
input.oninput = debounce(function() { // 这里如果用箭头函数,还是指向window,不能用箭头函数
// 业务逻辑,用一个打印代替
console.log(this.value)
}, 500)
function debounce(fn, delay) {
let t = null
return function() {
if (t !== null) {
clearTimeout(t)
}
t = setTimeout(() => {
// 执行业务逻辑,这里的this就是input 事件对象
console.log('this: ', this)
fn.call(this)
}, delay)
}
}
四、节流
有一种情形,滚动事件,会执行很多次。
window.onscroll = function() {
console.log('---------')
}
你看,不到一秒钟随便滚动一下就是100多次。
防抖:只执行最后一次;
节流:控制执行次数;让耗性能的方法减少执行次数;
和防抖是一样的写法
window.onscroll = throttle(() => {
// 这里是业务逻辑,this:window
console.log('..........')
}, 500)
function throttle(fn, delay) {
let flag = true
return function() {
if (flag) {
setTimeout(() => {
fn.call(this)
flag = true
}, delay)
}
flag = false
}
}