Javascript定时器

什么是定时器?

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的值

 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指向了全局对象 一个this指向了当前的元素 如何更改呢?

可以使用call方法来绑定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) 

     }

 }


当前this的值

这里通过了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

https://www.javascriptc.com/2283.html

https://www.javascriptc.com/2285.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容