前几天翻书,看到“避免双重求值”一节时有提到settimeout()、setinterval() 建议传入函数而不是字符串以做为第一个参数,因此这里总结一下settimeout()和setinterval()的区别,以及它们之间的相互模拟。前端
setTimeout(): 方法用于在指定的毫秒数后调用函数或计算表达式(函数更好,下面会解释为何函数更好!)。编程
语法:setTimeout(code,millisec) code:必需,要调用的函数后要执行的 JavaScript 代码串;millisec:必需,在执行代码前需等待的毫秒数。函数
setInterval() :方法用于按照指定的周期(以毫秒计)来循环调用函数或计算表达式,直到 clearInterval() 被调用或窗口关闭,由 setInterval() 返回的 ID 值可用做 clearInterval() 方法的参数。性能
语法:setInterval(code,millisec[,"lang"]) code:必需,要调用的函数或要执行的JavaScript 代码串;millisec:必须,周期性执行或调用 code 之间的时间间隔,以毫秒计。测试
从上面的定义能够看出,setTimeout(表达式,延时时间)在执行时,是在载入后延迟指定时间后,去执行一次表达式,次数是一次;而 setInterval(表达式,时间间隔)则不同,它从载入后,每隔指定的时间就执行一次表达式,只要窗口不关闭或 clearInterval() 调用就会无限循环下去。因此,二者是彻底是不同的,具体看下面测试代码和结果!spa
var intervalNum = 0, timeoutNum =0; function testsetInterval(){ var date = new Date(); console.log(date.getSeconds()); console.log("setInterval", intervalNum++); } function testsetTimeout(){ var date = new Date(); console.log(date.getSeconds()); console.log("setTimeout", timeoutNum++); } function testFuntion() { setInterval(function () { testsetInterval() },4000); //每4秒执行testsetInterval()一次 setTimeout(function () { testsetTimeout() },10000); //延迟10秒执行testsetTimeout()一次,只执行一次;单独的setTimeout()方法,须要有另外的方法去触发,如将其放在 body 的 onload事件方法内 }
从图中的结果能够看出,setInterval() 每4秒循环执行一次;然而setTimeout()在延迟10秒(37+10),执行一次后,再没执行!code
虽然二者不同,可是却能够相互模拟。具体使用那个,以具体的需求和场景具体分析,就像for循环能够模拟全部的循环同样(包括分支,以及do while同样)。通常状况下 setTimeout() 用于延迟执行某方法或功能;setInterval() 则通常用于刷新表单,对于一些表单的假实时指定时间刷新同步。对象
模拟 setInterval() :将 setTimeout() 包含在一个执行函数A中,而setTimeout() 本身的code执行函数又是A,而后在函数A外将函数A执行一次,即达到了循环执行的目的。blog
var intervalNum = 0; function testsetInterval() { var date = new Date(); console.log(date.getSeconds()); console.log("setInterval", intervalNum++); } function recursive() { testsetInterval(); setTimeout(function () { recursive() //递归,每隔4秒调用一次recursive() }, 4000) } function testFuntion() { recursive(); //在方法recursive外,调用一次recursive,以启动循环调用! }
循环执行,和setInterval()功能相同递归
模拟 setTimeout() :用 setInterval() 模拟 setTimeout() 很简单,在 setInterval() 执行一次后,马上关闭窗口(固然这是耍无赖)或者执行 clearInterval() 方法(这个靠谱点)。clearInterval() 须要在 setInterval()执行code方法内或其余地方执行,不能紧接着 setInterval() 后面执行,那样setInterval() 还没等到执行,就已经被干掉了。
var intervalNum = 0, clearId = 0; function testsetInterval(){ var date = new Date(); console.log(date.getSeconds()); console.log("setInterval", intervalNum++); clearInterval(clearId); //也能够在此执行 } function testFuntion() { clearId = setInterval(function () { testsetInterval(); //每隔4秒调用testsetInterval() // clearInterval(clearId); //能够在此执行 },4000); }
执行一次,关闭 setInterval(),和 setTimeout() 功能相同
最后,将书中看到的“避免双重求值”搬到这。以解释为何 “ 建议传入函数而不是字符串以做为第一个参数”。
setTimeout()、setInterval() 容许传入一个JS代码字符串并执行,然而在JS代码中执行另外一段JS代码时,代码首先会以正常的方式求值,而后在执行过程当中对包含于字符串中的代码发起另外一个求值运算,从而形成双重求值。它比直接包含的代码执行速度慢不少,缘由在于, 每次调用setTimeout()、setInterval()都会建立一个新的解释器/编译器实例。这必然使得代码执行速度变慢,效率下降,从而形成性能的浪费。因此建议传入函数而不是字符串来做为第一个参数。
我作了一个小测试。从 0 自加到 1亿,比较两种方式各自的实际耗时,代码以及测试结果以下:
var Timer ={ //从书上copy的一个JS代码时间分析对象 _data : {}, start:function (key) { Timer._data[key] = new Date(); }, stop:function (key) { var time = Timer._data[key]; if(time){ Timer._data[key] = new Date()-time; } }, getTime:function (key) { // return Timer._data[key]; console.log("time = "+ Timer._data[key]); } }; var intervalNum = 100000000, clearId = 100000000; function testsetInterval(){ //计算从0 加到 1亿,以 传入函数方式 执行 var temp = 0; while(intervalNum--){ if(temp !== 0){ temp = temp + intervalNum; }else { temp = (intervalNum+1) + intervalNum; } } console.log(temp); Timer.stop("testsetInterval"); //调用stop(),计算时间差 Timer.getTime("testsetInterval"); //将时间差值打印出来 } function testsetTimeout(){ //计算从0 加到 1亿,以 字符串方式 执行 var temp = 0; while(clearId--){ if(temp !== 0){ temp = temp + clearId; }else { temp = (clearId+1) + clearId; } } console.log(temp); Timer.stop("setTimeout"); //调用stop(),计算时间差 Timer.getTime("setTimeout"); //将时间差值打印出来 } function testFuntion() { Timer.start("testsetInterval"); //获取代码执行前的初始时间 setTimeout(function () { testsetInterval(); //每隔1秒调用testsetInterval() },1000); Timer.start("setTimeout"); setTimeout("testsetTimeout()",1000); //双重求值模式,每隔1秒调用testsetTimeout() }
,随着数据量的攀升,耗时的差距,更明显。下面以 0 自加到 10亿,再试下
看得出,“双重求值”对性能影响仍是蛮大的。虽然如今的CPU主频都至关高,处理数据至关快,而平时前端处理数据的数量级,也不见得能达到这么高,可是养成一种好的编程习惯和塑造提升代码性能的思想总没得错!