今日头条出的题目是写一个防抖函数,,根据用户输入的词语,提交后端进行模糊查询,按输入次序显示服务器返回结果。
原题目,面试官写的是注意防抖和次序。
他的要求是: 比如输入非常快的提交了三次 a, b,c 三个字母。由于服务器响应慢,客户端已经提交c查询,但是服务器才返回的b的查询结果(这个用闭包,其实来闭包也有问题,典型如何处理异常)。
面试官出的这道题的确很有代表性,前端开发中经常会遇到的问题,重复执行,表单重复提交,不管前后端开发都应该了解。这个题跟我大约2012年6月,腾讯网www.qq.com 首页改版上线,做的一个搜索模块很相似, 那时候在腾讯是我职业生涯中第一个前端开发工作。
当时这个项目,填了几个坑:
1.必须用原生javascript代码,腾讯网首页那时候可不让你引入jquery,引入一个大js这么大的访问量得浪费多少钱?:)
2.就是层遮盖的问题,输入关键字 关键字弹层是摊在输入框下方,那时候搜索框下面有flash,一不小心就被flash盖住
最坑的是还有飞来飞去的广告
3.其次就是后端返回的智能提示元素个数不相同,还有可能不返回,元素个数不同,就需要代理机制,冒泡到上层处理
4.另外就是css的问题,网页/图片/视频等栏目蓝色框是要覆盖的,各位看官仔细看,鼠标移动到“网页”下拉前下面是蓝色边框,移动过去后变没了。最先版本还记录用户鼠标从哪移的,点击了那个栏目,再跟关键词点击关联就跟复杂了。
5. 因为这个作为一个模块,就是css动态加载,动态加载简单,加载进来能用,不跟其他人冲突,处理起来也琐碎。
6.与分类栏目相关的关键词搜索,以及日志记录
7.异常处理
另外就是面试官说的那两个问题。我觉得最经典的就是防抖。虽然简单,但却经典,稍后介绍。
我做项目的那会儿,不知道是否是学艺不精,不知道有防抖的概念,那 下面就来介绍一下防抖。下面的代码测试一下,就会浪费很多资源执行程序,这里仅是输出了console.log(),对资源消耗不大,如果数据库批量查询影响就很大了。运行下面的程序,打开浏览器,滚动滚动条或者放大缩小窗口,浏览器控制台就会输出一堆log。
-
<html>
-
<head>
-
<meta charset="utf-8" />
-
<style>
-
body {
font-size:
12px;
height:
2000px;}
-
</style>
-
</head>
-
<body>
-
访问页面,打开浏览器控制台,用鼠标滚动或者调整浏览器大小观看效果
-
<script>
-
//滚动
-
window.onscroll =
function(){
-
console.log(
'滚动条位置:' +
document.body.scrollTop ||
document.documentElement.scrollTop)
-
}
-
//调整窗口大小
-
window.onresize =
() => {
-
console.log(
'窗口宽x高:' +
window.innerWidth+
'x'+
window.innerHeight)
-
}
-
</script>
-
</body>
-
</html>
函数防抖(debounce)
防抖函数:一个频繁触发的操作,在规定时间内,只让最后一次生效,前面的不生效。
看防抖函数debounce(fn,delay),delay参数是1秒,1秒调用防抖函数不管多少次,只执行一次。
防抖从字面的意思很好理解。典型的应用就是拍照,估计大家都有印象,不防抖的相机拍出来的照片有一堆重影。防抖就是让相机只拍一次。
上面项目需要防抖就是鼠标在 分类区域 移动的时候,如果用户从网页--》图片--》视频一下移动到音乐,只需要记录用户移动到了音乐即可。还有就是输入关键字非常快比如输入 菲律宾,实际查询菲律宾关键字就可以了。而不是输入“菲”查询一次“菲律”查询一次 “菲律宾”查询一次。
来一个最简单的例子,浏览器执行以下代码会发现console控制台输出很多次,但是计算资源是有限的,所以我们需要控制速度,这个好比游戏打怪,不等满血进度条完成,游戏无法开始。
上代码,看看防抖函数
-
/* 函数防抖,
-
@param fun 要防抖的函数名
-
@param delay 防抖间隔时间
-
*/
-
function debounce(fun, delay) {
-
return
(args) => {
-
clearTimeout(fun.id)
//清除定时器
-
fun.id = setTimeout(
()=> {
-
fun.call(
this, args)
-
}, delay)
-
}
-
}
经过上面的例子想必你也清楚了,什么是防抖,以及在前端如何防抖。对于后端那怎么防抖呢? 有兴趣的可以我在腾讯搜搜做积分系统的时候,写过的一篇文章,用memcache或者redis实际上实现的就是防抖
http://blog.sina.com.cn/s/blog_467eb8ca0100zmvb.html
那节流又是什么鬼东西呢?
函数节流(throttle)
函数节流
一个函数执行一次后,只有大于设定的执行周期后才会执行第二次。
节流函数,throttle(fun,delay)参数是1秒,则是不管持续多长时间,每秒执行一次,且每秒只执行一次。
这个打个比方,就是一盆水,慢慢流出来,而不是哐 一下直接倒了。这个很简单可以看示例中函数节流的那部分代码。
在上面那个 //函数节流
-
//函数节流
-
function throttle(fun,delay){
-
let valid =
true
-
return
function(args) {
-
if(!valid){
-
//休息时间 暂不接客
-
return
false
-
}
-
// 工作时间,执行函数并且在间隔期内把状态位设为无效
-
valid =
false
-
setTimeout(
() => {
-
fun.call(
this, args)
-
valid =
true;
-
}, delay)
-
}
-
}
在服务器端实现函数节流就很简单了,最好是放在异步执行,用sleep函数定时执行即可。
关于防抖和节流的函数 ,都写在下面了,大家在输入框快速输入字母,然后打开浏览器控制台,自行测试。
-
<html>
-
<head>
-
<meta charset="utf-8" />
-
<style>
-
body {
font-size:
12px;}
-
span {
width:
60px;
height:
22px;
display:inline-block;}
-
</style>
-
</head>
-
<body>
-
<span class="tip">无防抖测试
</span>
<input id="text" />
-
<br>
-
<span class="tip">防抖测试
</span>
<input id="input" />
-
<br>
-
<span class="tip">节流测试
</span>
<input id="throttle" />
<br />
-
-
</div>
-
<script>
-
//没有防抖请求
-
function ajax(content) {
-
console.log(
'ajax request data ' + content)
-
}
-
-
let input =
document.getElementById(
'text')
-
-
input.addEventListener(
'keyup',
function (e) {
-
ajax(e.target.value)
-
})
-
-
/*
-
*/
-
-
/* 函数防抖,
-
@param fun 要防抖的函数名
-
@param delay 防抖间隔时间
-
*/
-
function debounce(fun, delay) {
-
return
(args) => {
-
clearTimeout(fun.id)
//清除定时器
-
fun.id = setTimeout(
()=> {
-
fun.call(
this, args)
-
}, delay)
-
}
-
}
-
-
let inputb =
document.getElementById(
'input')
-
let debounceAjax = debounce(ajax,
1000)
//函数ajax加入防抖,间隔1秒
-
inputb.addEventListener(
'keyup',
function (e) {
-
debounceAjax(e.target.value)
-
})
-
-
-
//函数节流
-
function throttle(fun,delay){
-
let valid =
true
-
return
function(args) {
-
if(!valid){
-
//休息时间 暂不接客
-
return
false
-
}
-
// 工作时间,执行函数并且在间隔期内把状态位设为无效
-
valid =
false
-
setTimeout(
() => {
-
fun.call(
this, args)
-
valid =
true;
-
}, delay)
-
}
-
}
-
let throttleAjax = throttle(ajax,
1000)
-
-
let inputc =
document.getElementById(
'throttle')
-
inputc.addEventListener(
'keyup',
function(e) {
-
throttleAjax(e.target.value)
-
})
-
</script>
-
</body>
-
</html>
总结
- 函数防抖和函数节流都是防止某一时间频繁触发。
- 函数防抖是某一段时间内只执行最后一次,而函数节流是小间隔时间执行一次。
结合应用场景
- debounce 防抖
- window触发resize一次
- 滚动操作记录最终状态
- 鼠标不断点击触发,mousedown(单位时间内只触发一次)
- 输入参数校验,只需要校验一次就好
- throttle 节流
- search搜索联想,用户在不断输入值时,节约请求资源
- 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
- 监听window 的resize多次
- 计算鼠标移动距离
- canvas画图
- 射击游戏,单位时间点击按钮,单击鼠标发射一次子弹
转载:https://blog.csdn.net/robinhunan/article/details/106206316