我是一个比较笨的人,有些逻辑上不好拐大弯,一旦拐太大了容易撞车,搞不明白。今天我在做input[type=password]的过程中,需要对用户输入的密码进行格式检查,但我又不想用户每输入一次就检查一次,而是希望让程序发现用户输入停止1000毫秒后再检查(虽说只是前端验证,做debounce并不在性能上有多大优化,但我就想做),之前知道debounce函数,于是自己想试一下。
<input type="text" name="" value="" class="flex-2" @input="inputHandler($event)">
首先想到的是用lodash里自带的debounce函数,于是就有了
import { debounce } from 'lodash'
...
inputHandler(event){
let password = event.target.value
debounce(()=>{
let reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[\S]{8,32}$/;
if (reg.test(password) !== true) {
console.log(11111)
}
}, 1000)
},
结果发现console.log(11111)
根本没有执行。
无奈,请求同事,什么电梯的比喻、高阶函数、你应该把inputHandler放到debounce里面云云,因我比较笨,实在没听懂。
没辙,只好回家重新思索,追溯本源,这样让我好受一些。
前面提到,做debounce的主要意图,是想让用户输入间隔1秒后再去对密码格式进行检查,提到1秒后
,条件反射般想到的便应是:
setTimeout(()=>{
console.log('如何如何')
},1000)
撇开那些高深的东西,我们先把这段代码加到inputHandler中
inputHandler(event){
let password = event.target.value
setTimeout(()=>{
let reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[\S]{8,32}$/;
if (reg.test(password) !== true) {
console.log(11111)
}
},1000)
},
这时在input里输入,发现console.log(11111)
执行了,万里长城第一步。
接下来的问题,想必大家也知道,就是我在input里输入了3个字母,那么11111
就会在控制台里出现3次,输入6个字母则出现6次,这时并没有达到我们想要的debounce的目的。
而出现这个的原因是,每在input里输入一个字母,inputHandler
就会执行一次。输入6个字母,setTimeout
便执行6次,所以控制台出现6个11111
。
那我们直观的想,如果setTimeout可以只执行一次就好了。
于是,逻辑就变成,如果在1秒内又一次执行了inputHandler,那么就让上一次的setTimeout取消,并重新执行新的setTimeout。而正好,我们知道一个取消setTimeout的方法,即clearTimeout
。
通过阅读MDN文档,我们得知:
setTimeout返回值
timeoutID
是一个正整数,表示定时器的编号。这个值可以传递给clearTimeout()
来取消该定时。
于是我们知道,只要clearTimeout(setTimeout的返回值),就可以取消该定时。
我们新增一个变量timeoutID记录当前setTimeout的号码,以便在合适的时候执行clearTimeout(timeoutID):
inputHandler(event){
let password = event.target.value
let timeoutID = setTimeout(()=>{
let reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[\S]{8,32}$/;
if (reg.test(password) !== true) {
console.log(11111)
}
},1000)
},
以合理的逻辑推理,我们肯定要在inputHandler
执行的时候就清空上一次的setTimeout,我们将clearTimeout(timeoutID)加入到代码中:
inputHandler(event){
clearTimeout(timeoutID)
let password = event.target.value
let timeoutID = setTimeout(()=>{
let reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[\S]{8,32}$/;
if (reg.test(password) !== true) {
console.log(11111)
}
},1000)
},
但问题来了,此时timeoutID并没有声明,所以clearTimeout此处无效。更何况而且每次我们都重新声明了timeoutID,那么clearTimeout的是当前的setTimeout。
那么如何清空上一次的timeoutID呢?这里就要用到全局变量了:
let timeoutID
inputHandler(event){
clearTimeout(timeoutID)
let password = event.target.value
timeoutID = setTimeout(()=>{
let reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[\S]{8,32}$/;
if (reg.test(password) !== true) {
console.log(11111)
}
},1000)
},
当页面加载后,全局变量timeoutID就始终存在于内存中,第一次在input里输入字母时,因为timeoutID还未赋值,故第一次clearTimeout(timeoutID)虽然执行了但并无意义。
在let password = event.target.value
之后,代码将setTimeout的返回值赋值给timeoutID。只要我们在1秒内第二次在input内输入字母,那么第二次执行clearTimeout(timeoutID)
时,此时清除的timeoutID为上一次setTimeout的返回值。随着代码继续执行,便将第二次的setTimeout返回值赋给了timeoutID。如果此时不再input输入,那么1秒后,console.log(11111)
将执行。这时,我们就实现了debounce效果。
优化
虽然功能实现了,但是有一个丑陋的全局变量timeoutID,能不能重构代码不用全局变量呢?
我想到的是,写一个初始化函数init(),在里面赋值timeoutID,页面加载时调用init(),timeoutID就称为全局的了。
init(){
let timeoutID
}
未完待续。
网友评论