场景
注册页面的昵称输入框,平台规定昵称是唯一的,你在oninput里去后台查询昵称的唯一性,现在,你要优化它的查询次数。
1. 节流怎么做?
n毫秒内你只去后台检查一次。
2. 防抖怎么做?
依然是n毫秒内你只去后台检查一次,另,如果n毫秒内又发生了一次oninput,那么,你需要重置n。
实现
1. 节流的实现代码:
<input name="username" type="text" oninput="checkUsername(this)">
<script>
var isRuning = false;
function checkUsername(it) {
if (!isRuning) {
isRuning = true;
// 可以在这里写昵称检查
setTimeout(() => isRuning = false, 300)
}
}
</script>
2. 防抖的实现代码:
<input name="username" type="text" oninput="checkUsername(this)">
<script>
var handler;
function checkUsername(it) {
clearTimeout(handler);
handler = setTimeout(() => {
// 可以在这里写昵称检查
}, 300)
}
</script>
重构
几个月之后你又遇到一个场景,需要做鼠标跟随特效。你想到节流和防抖,于是,你提炼如下:
1. 节流
<input name="username" type="text" oninput="checkUsername(this)">
<script>
const throttle = (fun, wait) => {
let isRuning = false;
return (obj) => {
if (!isRuning) {
isRuning = true;
setTimeout(() => isRuning = false, wait)
fun(obj);
}
}
}
const checkUsername = throttle(x => {
// 可以在这里写昵称检查
}, 300);
</script>
2. 防抖
<input name="username" type="text" oninput="checkUsername(this)">
<script>
const debounce = (fun, wait) => {
let handle;
return (obj) => {
clearTimeout(handle);
handle = setTimeout(() => fun(obj), wait);
}
}
const checkUsername = debounce(x => {
// 可以在这里写昵称检查
}, 300);
</script>
另外的思考
又过了几个月,你看了一本书,书中提到自定义事件,你灵光乍现,是不是节流和防抖可以用自定义事件实现呢?
1. 节流
<input id="username" name="username" type="text">
<script>
const throttle = (eventTarget, eventName, wait) => {
let isRuning = false;
let event = new Event(eventName);
return (detail) => {
if(isRuning) return;
isRuning = true;
setTimeout(() => isRuning = false, wait);
event.detail = detail;
eventTarget.dispatchEvent(event);
}
}
let target = document.getElementById('username');
target.addEventListener('input', throttle(target, 'checkUsername', 300))
target.addEventListener('checkUsername', (event) => {
// 可以在这里写昵称检查
})
</script>
2. 防抖
<input id="username" name="username" type="text">
<script>
const debounce = (eventTarget, eventName, wait) => {
let handle;
let event = new Event(eventName);
return (detail) => {
event.detail = detail;
clearTimeout(handle);
handle = setTimeout(() => eventTarget.dispatchEvent(event), wait);
}
}
let target = document.getElementById('username');
target.addEventListener('input', debounce(target, 'checkUsername', 300))
target.addEventListener('checkUsername', (event) => {
// 可以在这里写昵称检查
})
</script>
总结
文字不多,代码偏多,如果没懂,拷贝下来试试就简单了。写法不止这些了,但是这种基于事件的思想我觉得很特别。基于事件的代码,可以更加解耦,可能别的代码也关心这个事件,但是不关心你昵称检查的代码。比如,可以封装一个ajax请求,成功或者失败都会触发事件,有的代码可能捕获这些事件来显示隐藏加载提示,有的代码希望记录日志。因为是基于事件,那么大家的代码互不影响。