我知道的setTimeout

473 阅读3分钟

我知道的setTimeout

什么是setTimeout

简单来说它是一个定时器,是浏览器提供的一个api,用于延迟执行代码

它的语法是这样的:

var timer=setTimeout(function[,delay,args1,agrs2,...])

var timer=setTimeout(code[,delay])

setTimeout的第一个参数可以是function,也可以是一个字符串类型的代码片段(类似于eval那样的)

参数

参数是否可选描述
function/code定时器要执行的函数主体,可以是函数,也可以是代码片段.(注意:不建议使用代码片段,因为跟eval一样不安全)
delay定时器需要延迟的时间毫秒数,如果没有设置默认为0
...args当定时器的第一个参数是函数时,将作为函数的参数传入

返回值

setTimeout返回一个int型的定时器id,用于后续的取消操作(clearTimeout)

function say(){
    console.log('你好')
}

let timerId=setTimeout(say,10*1000);

clearTimeout(timerId) //定时器被取消、不在执行

注意:clearInterval,clearTimeout都可以用于取消定时器无论定时器id来源setInterval,setTimeout

如:

var time1=setTimeout(fn,1000);
clearInterval(time1)//这是可以取消定时器的

var time1=setInterval(fn,1000);
clearTimeout(time1)//这也是可以的

带参数的setTimeout

function say(name,week){
    console.log(`你好,${name},今天是${week}`)
}

setTimeout(say,1000,'jojo','周五') //打印:你好,jojo,今天是周五

setTimeout中的this问题

其实这个问题比较简单、我们都知道正常情况下js中this指是的代码执行上下文的一个变量,有三种类型:

  • 全局上下文(全局作用域)
    • 无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。
  • 函数上下文(函数作用域)
    • 在函数内部,this的值取决于函数被调用的方式。也就是谁调用、就指向谁,函数中的this是动态分配,执行的时候才确定
  • eval

如:

function say(){
	console.log(this.name)
}

say();

//这里函数是直接执行的、没有直接调用者,所以this指向了window


var person={
    name:'123',
	say:say
}

person.say();

//这里执行say的主体是person,所以this指向person对象

再来看看setTimeout执行

var person={
    name:'123',
	say:function(){
        console.log(this.name)
    }
}
setTimeout(person.say,1000) //打印undefined

上面这段代码、看上去是person执行say方法

其实应该是这样的:

function setTimeout(){
    var fn=arguments[0];
    var args=[].slice.call(arguments,2);
    if(typeof fn === 'function'){
        fn.apply(globalThis,args)
    }
    ...
}

setTimeout(person.say,1000)

//所以setTimeout中的的this指向globalThis

以下方法可以绑定this

//包装函数
setTimeout(function(){
    person.say();
},1000)

//绑定this
var fn=person.say.bind(person);
setTimeout(fn,1000)

MDN提供了封装的函数

// Enable the passage of the 'this' object through the JavaScript timers

var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval;

window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
  return __nativeST__(vCallback instanceof Function ? function () {
    vCallback.apply(oThis, aArgs);
  } : vCallback, nDelay);
};

window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
  return __nativeSI__(vCallback instanceof Function ? function () {
    vCallback.apply(oThis, aArgs);
  } : vCallback, nDelay);
};

setTimeout执行时间

  • 最小延迟时间: >=4ms
    • 在浏览器中,setTimeout()/setInterval() 的每调用一次定时器的最小间隔是4ms,
  • 最大延迟时间: 2147483647 ms (大约24.8 天)
    • 由于浏览器数据存储机制导致(32位带符号整数存储)
  • 定时器延迟执行的时间会大于给定的时间(setTimeout中的delay参数)
    • JS事件循环机制导致定时器会在主任务执行完后才开始