-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JavaScript专题之跟着underscore学防抖 #22
Comments
建议将第四版和第五版换个顺序,在第四版中,由于func始终是异步执行的,return result返回一直是undefined,只有在第五版中immediate参数为true的情况下,result才会取到结果,所以建议换个顺序更加严谨一些。 |
@wcflmy 确实存在这个问题,非常感谢指出~ o( ̄▽ ̄)d |
谢谢您的文章,最近想梳理自己的知识,但是却不知道从哪里入手好,跟着您的文章走真的是事半功倍啊,很多以前的疑点豁然开朗,再次谢谢! |
@xxxgitone 我也在梳理自己的知识,与你共勉哈~ |
写得很好,在做动画时也经常用到这种方式,防止在一帧时间中(大概16ms)渲染多次。 function debounce(func) {
var t;
return function () {
cancelAnimationFrame(t)
t = requestAnimationFrame(func);
}
} |
@hujiulong requestAnimationFrame 确实是神器呐~ |
学习了 点赞, 一路学到这里 收获颇丰 |
第五版有一点不解,为什么要
直接执行不可以么,望解答,谢谢 |
@YeaseonZhang 直接执行当然可以呀,之所以 return result ,是考虑到 func 这个函数,可能有返回值,尽管这个功能,我们在实际的开发中基本用不到……但是作为一个工具库,underscore 考虑得会更齐全一点~ |
跟着大神涨姿势了,我看了一两个小时才算看明白😂 |
大神厉害了,学习了 |
请问,您用的什么录屏软件啊,QuickTime吗? |
@chenxiaochun 推荐一下我用的。mac 上,我在用Gifox, snagit。但其实,giphy 也不错 http://recordit.co/ 这个也不错,简单粗暴,就是没有配置选项 还有这个也是免费的,而且 windows mac 都支持。。收费除了 SnagIt(支持 windows 和 mac),还有 Gifox(只支持 mac) |
@jawil ,@mqyqingfeng 发现了另一款截屏录屏神器,推荐给两位啊。不仅功能强大,颜值也很高。http://jietu.qq.com/ |
@chenxiaochun 我用的是一个 mac 下的叫做 licecap 的录制 GIF 的小软件,免费而且不需要安装,直接打开就能用 |
用promise应该可以也可以返回setTimeout中回调函数的结果。 |
@stormqx 确实如此,用 promise 可以实现这个效果,不过 underscore 中没有实现 promise,所以这里也就没有使用 promise,不过说起来,ES6 系列中会讲到从零实现一个 promise ,欢迎关注哈~ |
写的很好 感谢赐教! |
非常感谢分享,受教了! |
good! |
为什么不来讲解 lodash 呢 |
学以致用,感谢楼主! /**
* Created by Administrator on 2017/10/30.
*/
function setResult(tag, content, color) {
if(tag && typeof tag == 'object') {
tag.innerHTML = content;
tag.style.color = color;
}
}
var validateEmail = function (e) {
// 邮箱正则
var reg = /^[a-z0-9]+(\w|_)+@+([a-z0-9]){2,4}.[a-z]{2,4}$/;
var currentValue = e.target.value;
var resultTag = document.getElementById('resultEmail'),
content = reg.test(currentValue) ? '邮箱正确' : '请输入正确的邮箱',
color = reg.test(currentValue) ? 'green' : 'red';
setResult(resultTag, content, color);
}
var validateMobile = function (e) {
// 手机号正则
var reg = /^1(3|4|5|7|8){1}[0-9]{9}$/
var currentValue = e.target.value;
var resultTag = document.getElementById('resultMobile'),
content = reg.test(currentValue) ? '手机号正确' : '请输入正确的手机号',
color = reg.test(currentValue) ? 'green' : 'red';
setResult(resultTag, content, color);
}
// 防抖
function debounce(func, wait) {
var timeOut;
return function () {
if(timeOut) {
clearTimeout(timeOut);
}
// 保存this上下文,参数
var that = this, args = arguments;
timeOut = setTimeout(function () {
func.apply(that, args);
}, wait)
}
}
document.getElementById('emailIpt').onkeyup = debounce(validateEmail, 1000);
document.getElementById('mobileIpt').onkeyup = debounce(validateMobile, 1000); |
被第四版绕晕了 |
想问一下,事件用js动态绑定(跟示例一样),就好使,如果是在html标签绑定就不好使,是什么原因呢? |
@YuFengjie4 第四版添加了 immediate 的功能,具体是觉得哪里没有理解呢? |
@wangenze267 使用 HTML 标签也是可以的,你可以把你的 demo 分享出来,我帮你看一下问题所在 <!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="IE=edge, chrome=1">
<title>debounce</title>
<style>
#container{
width: 100%; height: 200px; line-height: 200px; text-align: center; color: #fff; background-color: #444; font-size: 30px;
}
</style>
</head>
<body>
<div id="container" onmousemove="getUserAction()"></div>
<script>
var count = 1;
var container = document.getElementById('container');
function getUserAction() {
container.innerHTML = count++;
};
</script>
</body>
</html> |
看懂了看懂了
…------------------ 原始邮件 ------------------
发件人: "mqyqingfeng/Blog" ***@***.***>;
发送时间: 2021年11月30日(星期二) 上午10:01
***@***.***>;
***@***.******@***.***>;
主题: Re: [mqyqingfeng/Blog] JavaScript专题之跟着underscore学防抖 (#22)
@YuFengjie4 第四版添加了 immediate 的功能,具体是觉得哪里没有理解呢?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.
|
@Madaovo 这种方案只是第一次立即执行,之后还是会延迟执行。 |
这种更像是立即执行加节流,而不是防抖 |
最初看第四版本理解成immediate为true时,立即出发一次,然后每隔一定时间触发像截流一样,怎么看代码都理解不了,还以为博主写错了,后来看了评论后又仔细看了一遍,理解错immediate的作用力,是自己马虎了 |
你好 ,我不太明白 这段代码的作用
}, wait); |
鼠标只滑动一次不动的话会多执行一次,这个有优化方向吗 |
大佬,遇到理解不了是选择暂时跳过还是继续刚 |
@DefeatLaziness 可以再多花点时间理解,如果还是不行,可以暂时跳过,过一段时间再来看。 |
好咧,谢谢大佬 |
定时器的回调函数为什么不用箭头函数呢 |
我理解的是,当我们addEventListener 的时候注册的事件的 debounce 返回的function 而不是debounce, 返回的函数形成了闭包,所以返回的函数里面能访问该作用域里面的timeout. |
总感觉给个返回值逻辑不是很对,因为fn压根就没执行,但是debounce的逻辑执行了就会有undefined返回,会不会给debounce方法引入一个callback参数作为fn执行取返回值更好一点 // 防抖变形: 取fn返回值
function debounce(fn, time = 1000, callback = () => { }) {
let timer = null;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
callback(fn(...args));
}, time);
}
} |
我觉得没有问题 |
这个函数名叫throttle比较好吧,raf 也是根据帧率来的,高帧率屏幕时间会更短 |
事件里的函数如果有返回值是不是没什么用啊?怎么接收? |
这样的你应该在防抖结束之后将isFirst 设置为true,不然下一次就不会立即执行了 |
使用context保存this是没有必要的吧,直接在apply的地方写this也是一样的 |
这是来自QQ邮箱的假期自动回复邮件。
您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。
|
前言
在前端开发中会遇到一些频繁的事件触发,比如:
……
为此,我们举个示例代码来了解事件如何频繁的触发:
我们写个
index.html
文件:debounce.js
文件的代码如下:我们来看看效果:
从左边滑到右边就触发了 165 次 getUserAction 函数!
因为这个例子很简单,所以浏览器完全反应的过来,可是如果是复杂的回调函数或是 ajax 请求呢?假设 1 秒触发了 60 次,每个回调就必须在 1000 / 60 = 16.67ms 内完成,否则就会有卡顿出现。
为了解决这个问题,一般有两种解决方案:
防抖
今天重点讲讲防抖的实现。
防抖的原理就是:你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行,真是任性呐!
第一版
根据这段表述,我们可以写第一版的代码:
如果我们要使用它,以最一开始的例子为例:
现在随你怎么移动,反正你移动完 1000ms 内不再触发,我才执行事件。看看使用效果:
顿时就从 165 次降低成了 1 次!
棒棒哒,我们接着完善它。
this
如果我们在
getUserAction
函数中console.log(this)
,在不使用debounce
函数的时候,this
的值为:但是如果使用我们的 debounce 函数,this 就会指向 Window 对象!
所以我们需要将 this 指向正确的对象。
我们修改下代码:
现在 this 已经可以正确指向了。让我们看下个问题:
event 对象
JavaScript 在事件处理函数中会提供事件对象 event,我们修改下 getUserAction 函数:
如果我们不使用 debouce 函数,这里会打印 MouseEvent 对象,如图所示:
但是在我们实现的 debounce 函数中,却只会打印 undefined!
所以我们再修改一下代码:
到此为止,我们修复了两个小问题:
立刻执行
这个时候,代码已经很是完善了,但是为了让这个函数更加完善,我们接下来思考一个新的需求。
这个需求就是:
我不希望非要等到事件停止触发后才执行,我希望立刻执行函数,然后等到停止触发 n 秒后,才可以重新触发执行。
想想这个需求也是很有道理的嘛,那我们加个 immediate 参数判断是否是立刻执行。
再来看看使用效果:
返回值
此时注意一点,就是 getUserAction 函数可能是有返回值的,所以我们也要返回函数的执行结果,但是当 immediate 为 false 的时候,因为使用了 setTimeout ,我们将 func.apply(context, args) 的返回值赋给变量,最后再 return 的时候,值将会一直是 undefined,所以我们只在 immediate 为 true 的时候返回函数的执行结果。
取消
最后我们再思考一个小需求,我希望能取消 debounce 函数,比如说我 debounce 的时间间隔是 10 秒钟,immediate 为 true,这样的话,我只有等 10 秒后才能重新触发事件,现在我希望有一个按钮,点击后,取消防抖,这样我再去触发,就可以又立刻执行啦,是不是很开心?
为了这个需求,我们写最后一版的代码:
那么该如何使用这个 cancel 函数呢?依然是以上面的 demo 为例:
演示效果如下:
至此我们已经完整实现了一个 underscore 中的 debounce 函数,恭喜,撒花!
演示代码
相关的代码可以在 Github 博客仓库 中找到
专题系列
JavaScript专题系列目录地址:https://github.com/mqyqingfeng/Blog。
JavaScript专题系列预计写二十篇左右,主要研究日常开发中一些功能点的实现,比如防抖、节流、去重、类型判断、拷贝、最值、扁平、柯里、递归、乱序、排序等,特点是研(chao)究(xi) underscore 和 jQuery 的实现方式。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered: