欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

那些年,踩过的 setTimeout 坑

程序员文章站 2024-01-21 19:06:04
...

1. 前言

众所周知,在JavaScript中,我们可以使用setTimeout()函数设置定时器,时间到了之后,执行一个函数或者一段代码

2. 基本用法

setTimeout(func|code, delay)

第一个参数接受一个函数或者一段代码
第二个参数是延迟执行的时间

举例:

funtion say() {
	console.log('早上好')
}

setTimeout(say, 1000) // 1s后执行say函数
setTimeout('console.log('早上好')', 1000) // 1s后执行这段代码(不推荐使用, 有安全风险)

很简单吧?看似很简单,其实不然,setTimeout()这个函数埋了不少坑

3. 坑点一,this指向

看这一段代码

var name = 'lx'
var obj = {
    name: 'zmj',
    sayName: function() {
        console.log(this.name)
    }
}

setTimeout(obj.sayName, 1000); // 'lx'

this并没有指向obj,而是指向window对象

为什么呢?

因为setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。
这会导致,这些代码中包含的 this 关键字在非严格模式会指向 window (或全局)对象

说白来,obj.sayName这个函数并没有交给obj对象去执行,真正去调用它的是setTimeout()

对于this指向问题,主要是要观察是谁去调用它

3.1 解决方法

解决方法主要就是我们要把sayName这个函数交给obj去执行

1. 使用包装函数

var name = 'lx'
var obj = {
    name: 'zmj',
    sayName: function() {
        console.log(this.name)
    }
}

setTimeout(function() {
    obj.sayName()   
}, 1000);

setTimeout中执行的是外面的包装函数,真正调用sayName()的还是obj,所以这种方法解决了 this 的指向问题

2. 使用显式绑定

var name = 'lx'
var obj = {
    name: 'zmj',
    sayName: function() {
        console.log(this.name)
    }
}

setTimeout(obj.sayName.bind(obj), 1000);

^_^,我们强行把 obj 绑给sayName,this 不得不从,指向了 obj

4. 坑点二,异步

setTimeout(function() {
    console.log('吴亦凡:没吃');
}, 10);

for (var i = 0; i < 100000; i++) {
    console.log('李雪琴:中午好呀,吃饭了没?')
}

我们和 setTimeout 说好了,让它10ms之后就执行
结果,setTimeout非要等 for 循环执行结束后(时间已然过去好几秒,如果你电脑比较好,把循环次数设大一些),它才执行

如果你把for循环 改成 while(true),那么李雪琴小姐永远都听不到吴亦凡先生的回复了

原因当然是因为 setTimeout是一个异步任务
在 js 中,一段代码从上到下,遇到异步任务的时候,并不影响下面的代码块,js 先把所有的同步任务执行完毕后,再来执行异步任务

于是乎,思考一道题,下面的代码会打印出什么?

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

答案打印出了5个6

虽然这道题主要考察的是作用域,但也考察了异步相关的知识
具体原因和解决办法可以参考 for 循环中的setTimeout(function(){})异步问题

相关标签: setTimeout