细说setTimeout

当我最开始接触setTimeout的时候,没觉得这个函数有什么,后来在码代码、面试的过程才慢慢踩中一些坑,然后再去网上找了很多文章来看,发现setTimeout里面的水很挺深。借这篇文章,我希望能够较为全面的展现setTimeout常见的疑点,同时也帮助自己梳理思路。

setTimeout是什么

setTimeout(Func, time) 是指定一个时长(单位是毫秒),等待这一段时长过后调用Func函数。

setTimeout返回一个标识该定时器的id值,使用clearTimeout传入id值就可以提前清除该定时器。

setTimeout属于谁

我们在调用setTimeout常常是直接使用setTimeout(...),但其实setTimeout是某一个对象的方法。该对象就是全局对象。

而全局对象跟JS的运行环境有关。在浏览器中,全局对象是window对象。在nodeJs中,全局对象是global对象。

所以,在浏览器环境中,我们直接调用setTimeout是省略了window对象,其实在后台是window.setTimeout.

那么这里有一个问题:能不能使用非全局对象调用setTimeout方法呢?请看下面一段代码

    var obj = {};
    obj.setTimeout = setTimeout;
    obj.setTimeout(function(){
      console.log("111")
    },100);

你觉得这段代码能正常运行吗?

事实是在node环境中可以正常运行,但在chrome浏览器中会报错

Uncaught TypeError: Illegal invocation at test.html:51

可以看出浏览器下setTimeout只能由window对象调用

setTimeout的常见调用方式

setTimeout(func||code, delay)接收两个参数,第一个参数是延迟执行的函数名,或者一段代码,第二个参数是延迟的毫秒数

给几个示例

  setTimeout('console.log("hello world")', 1000)

  setTimeout(function(){
    console.log('hello world')
  }, 1000)

  var a = function () {
    console.log('hello world')
  }
  setTimeout(a, 1000)

既然setTimeout第一个参数既可以是函数名,又可以是字符串,那么setTimeout(func,10)与setTimeout('func()',10)这两种调用方式有何区别呢。
我们来看一段代码

var b = function () {
  console.log('outer')
}
function container () {
  var b = function () {
    console.log('inner')
  }
  setTimeout(b,1000)
  setTimeout('b()',5000)
}
container()

请问上面这段代码控制台会输出什么呢?

答案是先输出inner,再输出outer。

由此,我们可以看出第一个参数带引号和不带引号的区别在于其回调函数执行的作用域不同。

当是字符串时,引擎会调用eval()执行,其环境是全局,所以不能访问函数作用域内的局部变量。

当是函数时,则执行该函数,可以访问到该函数作用域链上的变量。

因此,建议setTimeout第一个参数使用函数比较安全。

有时候会希望往setTimeout的回调函数里面传参,可以像下面这样写

  var a = 3
  var b = 2
  var sum = function (first, second) {
    console.log(first + second)
  }
  setTimeout('sum(a,b)',3000);
  setTimeout(sum,6000,a,b);
  setTimeout(function(){
    sum(a,b)
  },9000)

setTimeout与this指针

在JS中,this一直指向调用当前函数代码的对象。在全局作用域中,this指向全局对象.

在setTimeout的回调函数中,其this指针始终指向window对象。我们跑一段程序看看效果

  var a = 'outer'
  var obj = {
    a: 'inner',
    getA: function () {
      console.log(this.a)
      setTimeout(function () {
        console.log(this.a)
      },1000)
    }
  }
  obj.getA() // 先打印inner,再打印outer

如果我们需要setTimeout回调函数的this指向正确的对象时,通常可以采用三种办法。

1.将this赋给另一个变量

  var a = 'outer'
  var obj = {
    a: 'inner',
    getA: function () {
      var that = this
      setTimeout(function () {
        console.log(that.a)
      },1000)
    }
  }
  obj.getA() // inner

这种方法是通过闭包去访问保存了this值的局部变量that

2.bind()

  var a = 'outer'
  var obj = {
    a: 'inner',
    getA: function () {
      setTimeout(function () {
        console.log(this.a)
      }.bind(this),1000)
    }
  }
  obj.getA() // inner

bind()可以将函数绑定在某个对象上

3.ES6的箭头函数

  var a = 'outer'
  var obj = {
    a: 'inner',
    getA: function () {
      setTimeout(() => {
        console.log(this.a)
      },1000)
    }
  }
  obj.getA() // inner

箭头函数中的this指针总是指向函数定义时的对象。

setTimeout与异步/event loop

通常情况下,我们使用setTimeout都是希望去异步的在未来某个时刻执行某段代码。
那么setTimeout是真的异步吗?setTimeout会如我们所愿的非常精确的去执行这些动作吗?要回答以上问题,我们需要先理解JS在浏览器下的执行模式。

首先,众所周知,JS是单线程执行的,异步明显和单线程不能共存。为了实现异步,浏览器采用了事件循环机制和任务队列。

任务队列里面保存了JS未来需要执行的任务。
事件循环是指浏览器会不停的从任务队列中取任务去执行,只有在当前任务执行完后才会继续从任务队列中取任务并执行。JS的执行就是这种循环模式。

而setTimeout并不是精确的在未来某个时间执行回调函数,而是在一段时间后将代码放到任务队列末尾。如果时间设置为0,则表示立即插入,而不是立即执行。
为了展示这种机制,我们看一段代码

  setTimeout(function () {
    console.log('first')
  },0)
  console.log('second')
  // 输出second first

因此,在一些情况下,setTimeout回调函数执行的时间并不精确等于我们所设定的时间,而是会大于该时间。

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

推荐阅读更多精彩内容

  • setTimeout通常用于在指定的时间之后调用函数或表达式,多用作计时器。用法如下: code:要调用的函数或表...
    叫我徐小星阅读 449评论 6 1
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,764评论 0 38
  • 你千里送鹅毛的时候 他那里夏日炎炎蝉鸣不倦 或说 你是如此不合时宜 而你 只记得 他说 想看鹅毛一样的大雪 所以 ...
    风和阅读 280评论 4 8
  • 人生不易,为什么还要如此艰难的活着? 马里奥从诞生的那一刻起,就一直在奔跑、拼命的奔跑。因为不管愿不愿意,摆在他眼...
    岚风的叶子阅读 204评论 0 0
  • 现在,2016年4月26日00:48分。成都的雨声很焦急,我睡不着了。 三十分钟前,他给我打来电话——睡...
    Amber悟空阅读 176评论 0 0