JavaScript setTimeout要理解


JavaScript setTimeout要理解

文章插图
 
setTimeout()的参数:
大家都知道setInterval()和setTimeout()可以接收两个参数,第一个参数是需要回调的函数,必须传入的参数,第二个参数是时间间隔,毫秒数,可以省略 。但其实他可以接收更多的参数,那么这些参数是干什么用的呢?从第三个参数开始,依次用来表示传入回调函数的参数 。
setTimeout(function(a,b){ console.log(0+a+b);//这里打印的是:7},1000,3,4);
注意:IE 9.0及以下版本,只允许setTimeout有两个参数,不支持更多的参数
如果想向回调函数传参,可以用bind() 。
setTimeout( function(a,b){}.bind(3,4), 1000 );clearTimeout
setTimeout函数,返回一个表示计数器编号的整数值,将该整数传入clearTimeout函数,就可以取消对应的定时器 。clearTimout()有以下语法: clearTimeout(timeoutID) 要使用 clearTimeout( ), 我们设定 setTimeout( ) 时, 要给予这 setTimout( ) 一个名称, 这名称就是 timeoutID , 我们叫停时, 就是用这 timeoutID来叫停, 这是一个自定义名称 。
var id1 = setTimeout(f,1000); //id1就是timeoutIDvar id2 = setInterval(f,1000); //id2就是timeoutIDclearTimeout(id1);clearInterval(id2);setTimeout()的this指向:
对于JAVAscript中的this指向问题,之前也是困扰了我好久,哎呀,哪儿有那么难嘛,其实一句话就是说:谁调用的就是指向谁啊!意思就是说调用的对象是谁this就是指向谁 。
var x = 1;var obj = { x: 2, y: function(){ console.log(this.x); }};setTimeout(obj.y,1000); // 1why?不是说了哪个对象调用的就是指向哪个对象的嘛,这里不是setTimeout函数调用了obj对象里面的y方法吗,那不还是被setTimeout调用了吗,对啊,没错啊,就是setTimeout调用的,但是setTimeout函数是属于window的,知道吧,所以setTimeout的对象是window,所以一切都明了了 。
var x = 1;var obj = { x: 2, y: function(){ console.log(this.x); }};setTimeout(obj.y.bind(obj),1000); // 2function Animal(login) { this.login = login; this.sayHi = function() { console.log(this.login); //undefined }}var dog = new Animal('John');setTimeout(dog.sayHi, 1000);等到dog.sayHi执行时,它是在全局对象中执行,但是this.login取不到值 。
setTimeout()之延迟时间为0
console.log('a');setTimeout(function(){ console.log('b');},0);console.log('c');console.log('d');// a // c // d // b我也不截图了 。知道为什么吗,理论上他延迟时间为0不是应该马上执行吗,不是的 。因为setTimeout运行机制说过,必须要等到当前脚本的同步任务和“任务队列”中已有的事件,全部处理完以后,才会执行setTimeout指定的任务 。 也就是说,setTimeout的真正作用是,在“任务队列”的现有事件的后面再添加一个事件,规定在指定时间执行某段代码 。setTimeout添加的事件,会在下一次Event Loop执行 。好吧,对事件循环不清楚的推荐看看JavaScript 运行机制详解:再谈Event Loop
事件循环中的setTimeout()
众所周知,Javascript引擎(以下简称JS引擎)是单线程的,在某一个特定的时间内只能执行一个任务,并阻塞其他任务的执行,也就是说这些任务是串行的 。这样的话,用户不得不等待一个耗时的操作完成之后才能进行后面的操作,这显然是不能容忍的,但是实际开发中我们却可以使用异步代码来解决 。
当异步方法比如这里的setTimeout(),或者ajax请求、DOM事件执行的时候,会交由浏览器内核的其他模块去管理 。当异步的方法满足触发条件后,该模块就会将方法推入到一个任务队列中,当主线程代码执行完毕处于空闲状态的时候,就会去检查任务队列,将队列中第一个任务入栈执行,完毕后继续检查任务队列,如此循环 。前提条件是主线程处于空闲状态,这就是事件循环的模型 。
setTimeout(function () { console.log("b");},0)console.log("a");// a// b原理,就是上面两段话当中解释的,执行时把setTimeout()放入任务队列中去,主线程执行完主线程的任务之后去任务队列里面执行setTimeout出来执行 。
setTimeout(function(){ console.log(1111);},0)while (true) {};这里控制台是永远不会输出东西的,因为主线程已经造成了死循环,主线程一直是不会空闲的,他不会到任务队列里面去执行拿setTimeout函数来执行 。
首先我们还是来看那道大家再熟悉不过的前端面试题:
for (var i = 1;i <= 5;i ++) { setTimeout(function timer() { console.log(i) },i * 1000)}我想刚入门的童鞋或者对JS作用域、闭包以及事件循环等概念不了解的童鞋会想当然的认为这道题的答案应该是:


推荐阅读