什么是定时器?
javascript延时执行的代码即为定时器。定时器通常有两个函数组成:
setTimeout
setInterval
定时器的作用场景?
轮播图,广告弹窗,动画, 异步等
定时器的用法和区别?
setTimeout()
setTimeout函数用来指定某个函数或某段代码,在多少毫秒后执行(只执行一次)
语法:
var timeoutld = setTimeout(func|code,delay)
timeoutld:定时器id 可以用于销毁这个定时器
func:用来延期执行的函数 可以在括号外定义或在定时器里面写
code:用来延期执行的代码
delay:延期执行多少毫秒
实例:
console.log(1)
setTimeout(console.log(2),1000)
console.log(3)
// 1
// 3
// 2
console.log(1)
var f = function(){
console.log(2)
}
console.log(3)
setTimeout(f,1000)
// 1
// 3
// 2
有一个需要注意的地方 如果调用的函数是对象的内部方法 那么this指向全局而不是对象的内部数据
var x = 1;
var obj = function()){
x:2,
y:function(){
console.log(this.x)
}
}
setTimeout(obj.y,1000)
// 1
由于匿名函数在全局作用域中执行,所以他指向的是全局变量中的x
解决办法:
1.在函数内部执行obj.y
var x = 1;
var obj = {
x:2,
y:function(){
console.log(this.x)
}
}
setTimeout(function(){obj.y()},1000)
// 2
2.使用bind方法 把obj.y绑定到obj上面
var x = 1;
var obj = {
x:2,
y:function(){
console.log(this.x)
}
}
setTimeout(obj.y.bind(obj),1000)
// 2
setInterval ()
setInterval 函数用来指定某个函数或某段代码,在多少毫秒后执行(无限次执行)
var x = 2;
setTimeout(function(){x++},1000)
console..log(x) // 3
console..log(x) // 4
clearTimeout(),clearInterval()
clearTimeout,clearInterval都用来清除定时器
var f = function(){console.log(1)}
var clear1 = setTimeout(f,1000)
var clear2 = setInterval(f,1000)
clearTimeout(clear1)
clearInterval(clear2)
防抖和节流
函数防抖debounce是指函数在某段时间内,无论触发了多少次回调,都只执行最后一次
为什么要用防抖函数呢,举个例子
有一个场景 用户在搜索框输入拼音的时候 搜索框会出现智能匹配 但是呢 用户清除一个字母在输入一个字母 会造成不必要的性能浪费(函数多次执行)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>
<body>
<input type="text" id="input" placeholder="请输入内容"/>
</body>
</html>
js
没有使用防抖函数的场景var input = document.getElementById("input")
var output = function(){
console.log(1)
}
input.addEventListener('keydown',output)
从上面可以看出 函数前前后后执行了8次 然而却没输入数据进去
第一版
var input = document.getElementById("input")
var output = function(){
console.log(1)
}
var debounce = function(func,wait){
let timeout; //定义一个定时器
return function(){
clearTimeout(timeout) //清除之前的定时器
timeout = setTimeout(func,wait) //设置定时器,延迟执行函数
}
}
input.addEventListener('keydown',debounce(output,1000))
上面的思路是监听键盘的按键,每当按下键盘时,隔1秒在执行函数。当用户不断输入时,都会取消掉之前设置的定时器而重新设置,这样就保证了只有最后一次操作才能触发
现在遇到了一个问题 如何通过this来获取当前对象的值呢?
先输出一下this的值
当前this的指向var debounce = function(func,wait){
let timeout;
return function(){
clearTimeout(timeout)
timeout = setTimeout(func,wait)
console.log(this)
}
}
var output = function(){
console.log(this)
}
可以看到 一个this指向了全局对象 一个this指向了当前的元素 如何更改呢?
可以使用call方法来绑定this指针
当前this的值var output = function(){
console.log(this.value)
}
var debounce = function(func,wait){
let timeout;
return function(){
var _this = this;
clearTimeout(timeout)
timeout = setTimeout(function(){
func.call(_this)
},wait)
}
}
这里通过了Function.prototype.call()的方法改变了this的指向问题,可以获取到当前输入的值了。现在我们再来优化一下
<script src="pinyin_dict_notone.js" type="text/javascript"></script>//文字转换拼音
<script src="pinyinUtil.js" type="text/javascript"></script> //文字转换拼音
var input = document.getElementById("input")
var result = ['张三','李四','王五']
var resultpinyin = [];
var pinyin = function(){
for(var i=0;i<result.length;i++){
var split = pinyinUtil.getPinyin(result[i]).replace(" ","")
resultpinyin.push(split)
}
}
var output = function(){
var parent = document.getElementsByClassName('result')[0].children[0]
parent.innerHTML = ""
var value = pinyinUtil.getPinyin(this.value).replace(" ","")
for(var i=0;i<resultpinyin.length;i++){
if(value[0] == resultpinyin[i][0] && resultpinyin[i].indexOf(value) != -1){
var li = document.createElement('li')
li.innerHTML = result[i]
var parent = document.getElementsByClassName('result')[0].children[0]
parent.append(li)
var _this = this
li.addEventListener('click',function(){
input.value = this.innerText
parent.innerHTML = ""
})
}
}
}
var debounce = function(func,wait){
let timeout;
return function(){
var _this = this;
clearTimeout(timeout)
timeout = setTimeout(function(){
func.call(_this)
},wait)
}
}
pinyin()
input.addEventListener('keydown',debounce(output,1000))
现在让我们来看一下节流函数 什么是节流呢?
如果某个事件持续触发,隔一段时间内,事件才会执行一次
让我们看个场景 现在许多网站有个返回顶部的功能 以简书为例
返回顶部现在 我们来考虑一下这个功能该如何实现呢?
首先呢 先输出一下当前浏览的位置
var scrollTop = document.body.scrollTop ||document.documentElement.scrollTop;
当浏览器滑动的时候 添加一个函数
function show(){
var scrollTop = document.body.scrollTop ||document.documentElement.scrollTop;
console.log(scrollTop)
}
window.onscroll = show
有个问题是 浏览器滚动时 函数执行频率太高了 这样很浪费性能 这时候 我们可以用节流函数来做优化
节流函数有两种方式实现:时间戳和定时器
第一版
时间戳
html
<body>
<div style="height: 2000px;"></div>
<div class="top">
回到顶部
</div>
</body>
function show(scroll){
if(scroll > 500){
document.getElementsByClassName('top')[0].style.display = "block"
}else{
document.getElementsByClassName('top')[0].style.display = "none"
}
console.log(scroll)
}
function throttle(func,wait){
var previous = 0;
return function(){
var nowtime = +new Date()
if(nowtime - previous > wait){ //判断一下触发事件的时候是否大于设定的时间周期
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
show(scrollTop)
previous = nowtime
}
}
}
window.addEventListener('scroll',throttle(show,500))
这里使用了时间戳的方法,第一次滚动时函数会立马执行,当滚动停止后没有办法在执行事件
未滚动前 滚动后可以看到 只执行了一次事件 现在看看定时器
滚动后function throttle(func,wait){
var time;
var previous = 0;
return function(){
if(!time){ //判断定时器是否存在
time = setTimeout(function(){ //设置定时器
time = null //清空定时器 直到下一次执行
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
func(scrollTop)
},wait)
}
}
可以看到 当滚动了一段距离后 函数才会执行一次 。
有没有一种办法 既能让函数在第一次滚动的时候马上执行 在滚动完后也开始执行呢?
function throttle(func,wait){
var time,scrollTop;
var previous = 0;
var start = function(scrollTop){
previous = +new Date()
time = null;
func(scrollTop)
}
return function(){
var now = +new Date()
//下次触发func的时间
var remaining = wait - (now - previous)
scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
//如果没有剩余的时间了
if(remaining <= 0 || remaining > wait){
if(time){
clearTimeout(time)
time = null;
}
previous = now;
func(scrollTop)
}else if(!time){
time = setTimeout(start,remaining)
}
}
}
这里判断一下上次执行的时间和这次触发的时间是否大于执行周期,如果大于则判断定时器是否存在,存在则清除之前设置的定时器,重新设置。
回到顶部功能
var backTop = document.getElementsByClassName('top')[0]
backTop.addEventListener('click',function(){
window.scrollTo(0,0);
})
参考链接
https://wangdoc.com/javascript/async/timer.html
网友评论