深入理解 setTimeout 与 setInterval

3,248 阅读5分钟

这是我参与8月更文挑战的第18天,活动详情查看8月更文挑战

前言

setTimeout 和 setInterval,相信大家都是经常用,或多或少都会用一些,但是可能会有很多同学,并不是特别地了解相关的比较深入的东西,仅仅停留在使用层面而已。今天我们就来深入了解 setTimeout 和 setInterval 吧。

setTimeout 的用法

mdn 的说明:

WindowOrWorkerGlobalScope 混合的 setTimeout() 方法设置一个定时器,该定时器在定时器到期后执行一个函数或指定的一段代码。

先来回顾下 setTimeout 怎么使用,我们看下下面的代码:

因为 setTimeout 方法是浏览器 window 对象提供的,所以我们可以把 window 给去掉。

setTimeout(function() {
	console.log('追梦玩家');
}, 1000);

// 等价于
/*
window.setTimeout(function() {
	console.log('追梦玩家');
}, 1000);
*/

结合上面 mdn 文档的说明,其实 setTimeout 也可以说是一个延时器,也就是说第二个参数,决定了多少毫秒之后,再去执行一个函数或者指定的一段代码。

setInterval 的用法

mdn 的说明:

WindowOrWorkerGlobalScopesetInterval() 方法重复调用一个函数或执行一个代码段,在每次调用之间具有固定的时间延迟。

同样的,我们来回顾下 setInterval 是怎样使用的,我们看下:

setInterval (function() {
    console.log('追梦玩家');
}, 1000);

上面代码,其实就是每隔 1000 毫秒,然后去执行对应的函数,打印 "追梦玩家"。

第一个参数可以是字符串

相信大家平时使用 setTimeout 的话,第一个参数都是传函数。传字符串,这种形式,大家使用的应该比较少,那我们来了解一下:

setTimeout("console.log('追梦玩家');", 1000);

这种方式,见得比较少,为什么不用呢?

delay毫秒之后编译和执行字符串 (使用该语法是不推荐的, 原因和使用 eval()一样,有安全风险)。

其实如果第一个参数是字符串的话,相当于是执行 eval() 方法来执行 code。

setTimeout 的返回值

注:这里举例的是 setTimeout,对于 setInterval 也同样适用的。 setTimeout 的返回值 timeroutID,是一个正整数,表示定时器的编号。

var timer1 = setTimeout(function() {
  console.log('追梦玩家');
}, 1000);
var timer2 = setTimeout(function() {
  console.log('追梦玩家');
}, 1000);
var timer3 = setTimeout(function() {
  console.log('追梦玩家');
}, 1000);

console.log(typeof timer1);
console.log(timer1, timer2, timer3);

输出结果,可以看下图

image.png

有个问题,那我们怎么取消定时器呢?

其实就是使用到上面执行方法的返回值 timeroutID,可以传递给 clearTimeout 来取消定时器。

var timer1 = setTimeout(function() {
  console.log('追梦玩家');
}, 1000);
clearTimeout(timer1);

在控制台输入这个代码,并且执行,你会发现控制台并没有打印出"追梦玩家"。说明已经取消掉定时器了。

注:使用 setInterval 方法,要取消定时器,可以使用 clearInterval 方法。使用跟 clearTimeout 一样。

参数传参问题

那使用 setTimeout 方法的时候,我们想要传参,应该怎么做呢?

我们先来尝试一下:

var timer1 = setTimeout(function (...args) {
  console.log('args', args);
}, 1000, 1, 2, 3, 4, 5, 6);

上面这个方式,大家觉得怎么样?看起来是可以实现传参了。那有没有更优雅的方式呢?有的。

function test(...args) {
  console.log('args', args);
}
var timer1 = setTimeout(function () {
  test(1, 2, 3, 4, 5, 6);
}, 1000);

其实就是在 setTimeout 的第一个函数里面,放入一个 test 函数并且执行,传入对应的参数。

this 指向

我们先来了解下什么是隐式丢失?

隐式丢失是指被隐式绑定的函数丢失绑定对象,这种情况容易出错却又常见,为什么呢?因为 JavaScript 中函数可以作为参数,也可以作为返回值被四处传播,传着传着就丢了 this 指向,被传的晕头转向了。

我们看个例子:

var obj = {
  a: function() {
    console.log(this);
  }
}
var test = obj.a;
test();

输出结果是 window 对象,如果是直接执行 obj.a 这个方法,不进行赋值操作的话,输出结果是 obj 对象,其实就是在赋值的过程中,发生了隐式丢失。

使用 setTimeout/setInterval 方法,也会出现隐式丢失。

var obj = {
  a: function() {
    console.log(this);
  }
};
​
setTimeout(obj.a, 1000);

输出结果也是 window 对象,那怎么解决这个 this 指向的问题呢?

有两种方式:

  1. 使用 bind 方法将 obj.a 方法的 this 绑定到 obj 上
  2. 将 obj.a 函数放置在定时器中的匿名函数中执行

具体代码,可以看下面

// 方式1
setTimeout(obj.a.bind(obj), 1000);

// 方式2
setTimeout(function() {
  obj.a();
}, 1000);

时间间隔

HTML5 标准规定

  • setTimeout 的最短时间间隔是4毫秒
  • setInterval 的最短间隔时间是10毫秒, 也就是说,小于10毫秒的时间间隔会被调整到10毫秒

后台模式

大多数电脑显示器的刷新频率是60HZ,大概相当于每秒钟重绘60次。因此,最平滑的动画效的最佳循环间隔是1000ms/60,约等于16.6ms。

为了节电,对于那些不处于当前窗口的页面,浏览器会将时间间隔扩大到1000毫秒。

另外,如果笔记本电脑处于电池供电状态,Chrome和IE9以上的版本,会将时间间隔切换到系统定时器,大约是16.6毫秒。

写到最后

本文主要让大家理解了 setTimeout 和 setInterval 的相关知识:

  • 第一个参数可以是字符串
  • setTimeout 的返回值
  • 参数传参问题
  • this 指向
  • 时间间隔

文中如有错误,欢迎在评论区指正,如果这篇文章帮助到了你或者喜欢,欢迎点赞和关注。