从新认识一
平常,
setTimeout
函数吸收两个参数,第一个参数func|code是将要推延实行的函数名或许一段代码(引擎内部运用eval函数,将字符串转为代码),第二个参数delay是推延实行的毫秒数。然则,setTimeout
还可以增加更多参数
。第二个今后的参数都将作为 推延实行函数的 参数 传入。
// 传入4个参数
setTimeout(function(a,b){
// a=1,b=2
console.log(a+b);
},1000,1,2);
// 3
从新认识二
IE 9.0及以下版本,只允许setTimeout有两个参数,不支持更多的参数。有以下解决要领。
第一种是在一个匿名函数内里,让回调函数带参数运转,再把匿名函数输入setTimeout。
setTimeout(function() {
myFunc("one", "two", "three");
}, 1000);
上面代码中,myFunc是真正要推延实行的函数,有三个参数。假如直接放入setTimeout,低版本的IE不能带参数,所以可以放在一个匿名函数。
第二种解决要领是运用bind要领,把过剩的参数绑定在回调函数上面,天生一个新的函数输入setTimeout。
setTimeout(function(arg1){}.bind(undefined, 10), 1000);
上面代码中,bind要领第一个参数是undefined,示意将原函数的this绑定全局作用域,第二个参数是要传入原函数的参数。它运转后会返回一个新函数,该函数不带参数。
从新认识三
假如被setTimeout推延实行的
回调函数是某个对象的要领
,那末该要领中的this
关键字将指向全局环境
,而不是定义时地点的谁人对象。
举例1
var x = 1;
var o = {
x: 2,
y: function(){
console.log(this.x);
}
};
setTimeout(o.y,1000);
// 1
// 上面代码输出的是1,而不是2,这示意o.y的this所指向的已不是o,而是全局环境了。
举例2
function User(login) {
this.login = login;
this.sayHi = function() {
console.log(this.login);
}
}
var user = new User('John');
setTimeout(user.sayHi, 1000);
// undefined
// 上面代码只会显现undefined,因为比及user.sayHi实行时,它是在全局对象中实行,所以this.login取不到值。
解决办法:
要领一 将user.sayHi放在函数中实行,sayHi是在user作用域内实行,而不是在全局作用域内实行,所以可以显现准确的值
setTimeout(function() {
user.sayHi();
}, 1000);
要领二 运用bind要领,将绑定sayHi绑定在user上面
setTimeout(user.sayHi.bind(user), 1000);
从新认识四
HTML 5规范划定,
setTimeout的最短时候距离是4毫秒。
为了节电,关于那些不处于当前窗口的页面,浏览器会将时候距离扩大到1000毫秒
。别的,假如笔记本电脑处于电池供电状况,Chrome和IE 9以上的版本,会将时候距离切换到体系定时器,大约是15.6毫秒。setInterval的最短距离时候是10毫秒
,也就是说,小于10毫秒的时候距离会被调解到10毫秒。
从新认识五
setInterval函数的用法与setTimeout完全一致。
setInterval
指定的是“最先实行”之间的距离,并不斟酌每次使命实行自身所斲丧的时候。因而现实上,两次实行之间的距离会小于指定的时候。比方,setInterval
指定每100ms
实行一次,每次实行须要5ms
,那末第一次实行完毕后95毫秒
,第二次实行就会最先。假如某次实行耗时迥殊长,比方须要105毫秒,那末它完毕后,下一次实行就会马上最先
。为了确保两次实行之间有牢固的距离,可以不必setInterval,而是每次实行完毕后,运用setTimeout指定下一次实行的详细时候。
写个demo,确保 下一个对话框总是在封闭上一个对话框今后2000毫秒弹出:
var i=1;
var timer=setTimeout(function(){
alert(i);
timer =setTimeout(arguments.callee,2000);
},2000)
用setTimeout模拟了setInterval
function interval(func,wait){
var interv=function(){
func.call();
setTimeout(interv,wait);
}
setTimeout(interv,wait);
}
interval(function(){console.log(1)},1000)
从新认识六
setTimeout和setInterval返回的整数值是一连的,也就是说,第二个setTimeout要领返回的整数值,将比第一个的整数值大1。运用这一点,可以写一个函数,作废当前一切的setTimeout。
clearTimeout现实运用的例子。有些网站会及时将用户在文本框的输入,经由过程Ajax要领传回服务器,jQuery的写法以下。
$('textarea').on('keydown', ajaxAction);
如许写有一个很大的瑕玷,就是假如用户一连击键,就会一连触发keydown事宜,形成大批的Ajax通讯。这是不必要的,而且极能够会发作机能题目。准确的做法应该是,设置一个门坎值,示意两次Ajax通讯的最小距离时候。假如在设定的时候内,发作新的keydown事宜,则不触发Ajax通讯,而且从新最先计时。假如过了指定时候,没有发作新的keydown事宜,将举行Ajax通讯将数据发送出去。
debounce 防抖动要领
function debounce(fn, delay){
var timer = null; // 声明计时器
return function(){
//保留当前作用域的 this和 arguments
var context = this;
var args = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(context, args);
}, delay);
};
}
// 用法示例
$('textarea').on('keydown', debounce(ajaxAction, 2500))
从新认识七
setTimeout和setInterval的运转机制是,将
指定的代码移出本次实行
,比及下一轮Event Loop
时,再搜检是不是到了指定时候。假如到了,就实行对应的代码;假如不到,就比及再下一轮Event Loop时从新推断。这意味着,
setTimeout和setInterval指定的代码,必需比及本轮Event Loop的一切同步使命都实行完,再比及本轮Event Loop的“使命行列”的一切使命实行完,才会最先实行
。因为前面的使命究竟须要若干时候实行完,是不确定的,所以没有办法保证,setTimeout和setInterval指定的使命,一定会根据预定时候实行。
setIntervel具有积累效应
,假如某个操纵迥殊耗时,超过了setInterval的时候距离,排在背面的操纵会被积累起来,然后在很短的时候内一连触发
,这能够或形成机能题目(比方集合发出Ajax要求)。
setInterval(function () {
console.log(2);
}, 1000);
(function () {
sleeping(3000);
})();
// 2,2,2
// 2
// ...
效果就是比及第二行语句运转完成今后,马上一连输出三个2,然后最先每隔1000毫秒,输出一个2。
从新认识八
比及当前剧本的同步使命和“使命行列”中已有的事宜,悉数处理完今后,才会实行
setTimeout
指定的使命。也就是说,setTimeout的真正作用是,在“音讯行列”的现有音讯的背面再增加一个音讯,划定在指定时候实行某段代码。setTimeout增加的事宜,会鄙人一次Event Loop实行。
setTimeout(f, 0)
将第二个参数设为0,作用是让f在现有的使命(剧本的同步使命和“音讯行列”指定的使命)一完毕就马上实行。也就是说,setTimeout(f, 0)的作用是,尽量早地实行指定的使命。而并非会马上就实行这个使命。
setTimeout(f, 0)指定的使命,最早也要到下一次Event Loop才会实行
setTimeout(function() {
console.log("Timeout");
}, 0);
function a(x) {
console.log("a() 最先运转");
b(x);
console.log("a() 完毕运转");
}
function b(y) {
console.log("b() 最先运转");
console.log("传入的值为" + y);
console.log("b() 完毕运转");
}
console.log("当前使命最先");
a(42);
console.log("当前使命完毕");
// 当前使命最先
// a() 最先运转
// b() 最先运转
// 传入的值为42
// b() 完毕运转
// a() 完毕运转
// 当前使命完毕
// Timeout
从新认识九
可以调解事宜的发作递次。
例子1
网页开辟中,某个事宜先发作在子元素,然后冒泡到父元素,即子元素的事宜回调函数,会早于父元素的事宜回调函数触发。假如,我们先让父元素的事宜回调函数先发作,就要用到setTimeout(f, 0)。
var input = document.getElementsByTagName('input[type=button]')[0];
input.onclick = function A() {
setTimeout(function B() {
input.value +=' input';
}, 0)
};
document.body.onclick = function C() {
input.value += ' body'
};
上面代码在点击按钮后,先触发还调函数A,然后触发函数C。在函数A中,setTimeout将函数B推延到下一轮Loop实行,如许就起到了,先触发父元素的回调函数C的目标了。
例子2
用户在输入框输入文本,keypress事宜会在浏览器吸收文本之前触发。想在用户输入文本后,马上将字符转为大写。然则现实上,它只能将上一个字符转为大写,因为浏览器此时还没吸收到文本。
document.getElementById('my-ok').onkeypress = function() {
var self = this;
setTimeout(function() {
self.value = self.value.toUpperCase();
}, 0);
}
例子3
var div = document.getElementsByTagName('div')[0];
// 写法一 形成浏览器“梗塞”,因为JavaScript实行速率远高于DOM,会形成大批DOM操纵“聚集”
for (var i = 0xA00000; i < 0xFFFFFF; i++) {
div.style.backgroundColor = '#' + i.toString(16);
}
// 写法二
var timer;
var i=0x100000;
function func() {
timer = setTimeout(func, 0);
div.style.backgroundColor = '#' + i.toString(16);
if (i++ == 0xFFFFFF) clearTimeout(timer);
}
timer = setTimeout(func, 0);
从新认识十
一般使命(task)与微使命(microtask)。它们的区分在于,“一般使命”鄙人一轮Event Loop实行,“微使命”在本轮Event Loop的一切使命完毕后实行。
一般使命:
setTimeout
setInterval
setImmediate
I/O
种种事宜(比方鼠标单击事宜)的回调函数
微使命:
process.nextTick
Promise
console.log(1);
setTimeout(function() {
console.log(2);
}, 0);
Promise.resolve().then(function() {
console.log(3);
}).then(function() {
console.log(4);
});
console.log(5);
// 1
// 5
// 3
// 4
// 2