关于JS中的setTimeout()

1、setTimeout()基础

setTimeout函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器。

var timerId = setTimeout(func|code, delay)

上面代码中,setTimeout函数接受两个参数,第一个参数func|code是将要推迟执行的函数名或者一段代码,第二个参数delay是推迟执行的毫秒数。·

需要注意的是,推迟执行的代码必须以字符串的形式,放入setTimeout,因为引擎内部使用eval函数,将字符串转为代码。如果推迟执行的是函数,则可以直接将函数名,放入setTimeout。一方面eval函数有安全顾虑,另一方面为了便于JavaScript引擎优化代码,setTimeout方法一般总是采用函数名的形式,就像下面这样。

function func(){
  console.log(2);
}
setTimeout(func,1000);
// 或者
setTimeout(function (){console.log(2)},1000);

2、关于setTimeout执行的几个代码片段,看一下执行结果是什么?

for (var i = 0; i < 5; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000 * i);
}

输出:setTimeout 会延迟执行,那么执行到 console.log 的时候,其实 i 已经变成 5 了,所以“应该是开始输出一个 5,然后每隔一秒再输出一个 5,一共 5 个 5。”

怎么改才能输出 0 到 4 呢?(用闭包)

for (var i = 0; i < 5; i++) {
  (function(i) {
    setTimeout(function() {
      console.log(i);
    }, i * 1000);
  })(i);
}

输出:0、1、2、3、4。

如果改成这个样子,输出什么:

for (var i = 0; i < 5; i++) {
  setTimeout((function(i) {
    console.log(i);
  })(i), i * 1000);
}

输出:这里给 setTimeout 传递了一个立即执行函数。setTimeout 接受函数或者字符串作为参数,那么这里立即执行函数应该是个 undefined ,也就是说等价于:·setTimeout(undefined, ...); 而立即执行函数会立即执行,所以应该立马输出0到4。


3、setTimeout()中回调函数中的this

如果被setTimeout推迟执行的回调函数是某个对象的方法,那么该方法中的this关键字将指向全局环境,而不是定义时所在的那个对象。

var x = 1;

var o = {
  x: 2,
  y: function(){
    console.log(this.x);
  }
};
setTimeout(o.y,1000);// 1

上面代码输出的是1,而不是2,这表示o.y的this所指向的已经不是o,而是全局环境了。

再看一个不容易发现错误的例子。·

function User(login) {
  this.login = login;
  this.sayHi = function() {
    console.log(this.login);
  }
}
var user = new User('John');
setTimeout(user.sayHi, 1000);

上面代码只会显示undefined,因为等到user.sayHi执行时,它是在全局对象中执行,所以this.login取不到值。

为了防止出现这个问题,一种解决方法是将user.sayHi放在匿名函数中执行。

setTimeout(function() {
  user.sayHi();
}, 1000);

上面代码中,sayHi是在user作用域内执行,而不是在全局作用域内执行,所以能够显示正确的值。

另一种解决方法是,使用bind方法,将绑定sayHi绑定在user上面。

setTimeout(user.sayHi.bind(user), 1000);

4、setTimeout传参数

有如下要求的例子,每间隔5000*i 秒执行一段程序

function send_ajax(i){
    console.log(new Date()+" "+i);
}

for(var i=0;i<5;i++){
    setTimeout(send_ajax(i),i*5000);
}

实际输出,如下没有达到我们的目标。因为你传递给了setTimeout一段函数调用send_ajax(i)的返回值 ,在本例中相当于setTimeout(undefine,arg1), 而send_ajax(i)会立即执行,随后什么也不会再发生。

Fri Oct 05 2018 16:11:42 GMT+0800 (中国标准时间) 0
Fri Oct 05 2018 16:11:42 GMT+0800 (中国标准时间) 1
Fri Oct 05 2018 16:11:42 GMT+0800 (中国标准时间) 2
Fri Oct 05 2018 16:11:42 GMT+0800 (中国标准时间) 3
Fri Oct 05 2018 16:11:42 GMT+0800 (中国标准时间) 4

因为setTimeout接收函数名或代码片段,所以改成如下的形式是可以的:

​
function send_ajax(i){
    console.log(new Date()+" "+i);
}

for(var i=0;i<5;i++){ 
	setTimeout("send_ajax("+i+")",i*5000);
}

输出结果如下,基本满足了我们的要求:

Fri Oct 05 2018 21:39:25 GMT+0800 (中国标准时间) 0
Fri Oct 05 2018 21:39:30 GMT+0800 (中国标准时间) 1
Fri Oct 05 2018 21:39:35 GMT+0800 (中国标准时间) 2
Fri Oct 05 2018 21:39:40 GMT+0800 (中国标准时间) 3
Fri Oct 05 2018 21:39:45 GMT+0800 (中国标准时间) 4

为了把每次调用的参数用一种较为优雅的形式来传递到setTimeout的执行函数,结合本文章前面第一节分析的那样,可以使用闭包。

function send_ajax(i){
    console.log(new Date()+" "+i);
}
for(var i=0;i<5;i++){ 
	(function(i){
		setTimeout(function(){send_ajax(i)},i*5000);
	})(i);
}
输出:(因为使用闭包,使用局部变量保持作用域外的一个引用)
Fri Oct 05 2018 21:47:00 GMT+0800 (中国标准时间) 0
Fri Oct 05 2018 21:47:05 GMT+0800 (中国标准时间) 1
Fri Oct 05 2018 21:47:10 GMT+0800 (中国标准时间) 2
Fri Oct 05 2018 21:47:15 GMT+0800 (中国标准时间) 3
Fri Oct 05 2018 21:47:20 GMT+0800 (中国标准时间) 4

或者:

function send_ajax(i){
    console.log(new Date()+" "+i);
}
function _send_ajax(_i){
    return function(){
             send_ajax(_i);
       }
}
for(var i=0;i<5;i++){ 
	setTimeout(_send_ajax(i),i*5000);
}
输出:(还是使用闭包的方式来解决,虽然传递给了一个立即执行的函数,但是这个函数的返回值却又是一个函数,保留了调用时的局部变量i)
VM5549:2 Fri Oct 05 2018 22:11:07 GMT+0800 (中国标准时间) 0
VM5549:2 Fri Oct 05 2018 22:11:12 GMT+0800 (中国标准时间) 1
VM5549:2 Fri Oct 05 2018 22:11:17 GMT+0800 (中国标准时间) 2
VM5549:2 Fri Oct 05 2018 22:11:22 GMT+0800 (中国标准时间) 3
VM5549:2 Fri Oct 05 2018 22:11:27 GMT+0800 (中国标准时间) 4

也附上经常容易出错的另外两种写法:

错误写法一:

function send_ajax(i){
    console.log(new Date()+" "+i);
}
for(var i=0;i<5;i++){ 
	setTimeout(function(){send_ajax(i)},i*5000);
}
输出:(因为setTimeout 会延迟执行,那么执行到 console.log 的时候,其实 i 已经变成 5 了)
Fri Oct 05 2018 21:44:27 GMT+0800 (中国标准时间) 5
Fri Oct 05 2018 21:44:32 GMT+0800 (中国标准时间) 5
Fri Oct 05 2018 21:44:37 GMT+0800 (中国标准时间) 5
Fri Oct 05 2018 21:44:42 GMT+0800 (中国标准时间) 5
Fri Oct 05 2018 21:44:47 GMT+0800 (中国标准时间) 5

错误写法二:

function send_ajax(i){
    console.log(new Date()+" "+i);
}
for(var i=0;i<5;i++){ 
	setTimeout(function(i){send_ajax(i)},i*5000);
}
输出:(因为setTimeout的函数的this指针是全局的,所以i在全局范围内执行是undefined)
Fri Oct 05 2018 21:44:01 GMT+0800 (中国标准时间) undefined
Fri Oct 05 2018 21:44:06 GMT+0800 (中国标准时间) undefined
Fri Oct 05 2018 21:44:11 GMT+0800 (中国标准时间) undefined
Fri Oct 05 2018 21:44:16 GMT+0800 (中国标准时间) undefined
Fri Oct 05 2018 21:44:21 GMT+0800 (中国标准时间) undefined

所以对setTimeout中执行函数进行传参数时推荐的做法是使用“闭包”。
 

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值