setTimeout简介
Javascript是单线程语言,不过允许通过设置超时调用(setTimeout)和间歇调用(setInterval)来调度代码在特定时刻执行。这两个方法都属于window对象,接受两个参数:第一个参数是要执行的代码(一般为函数),第二个是执行代码前需要等待的时间(ms)。
setTimeout :第二个参数,即表示需要等待的时间,但经过该时间后指定的代码不一定会立即执行。因为JS是单线程的,为了控制要执行的代码,就有一个JS任务队列,这些任务会按照它们被添加到队列中的顺序去执行。而setTimeout的第二个参数就是指定经过多长时间将第一个参数指定的任务添加到队列中。如果任务队列是空的,那么新添加的任务会立即执行;否则它就要等前面的任务执行完了以后才能开始执行。
// 休眠函数
let sleep=function(n){
let start=new Date().getTime();
while(true){
if(new Date().getTime()-start>n){
break;
}
}
console.log('wake up.')
}
// 超时调用
var globalAttribute='global';
let tmId=setTimeout(()=>console.log(this.globalAttribute),2500);
for(let i=0;i<5;i++){
setTimeout(()=>console.log(i),1000);
sleep(1000);
}
注意for语句中使用了let声明i(ES6语法),不同于通过var声明。所以i的值不是for语句结束时的值。
从以上例子可以看出,两个setTimeout都没有按设定的时间执行,因为任务队列中有for循环这个任务(里面的sleep函数比较耗时)仍在执行,只有等它执行完毕,才会执行超时调用中设置的任务。
setInterval :与setTimeout 类似,只是会每隔一段时间就会执行,在不受干涉的情况下会一直执行,所以要使用clearInterval取消。
介绍完setTimeout ,下面才是重头戏。
函数节流:高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率。(例如一直拖动进度条,如果与scroll绑定的代码不加控制,那只要拖动,相应代码就会高频执行;使用节流后,可以设置为特定时间(如500ms)才执行一次scroll事件处理代码)。
函数防抖:频繁触发的情况下,只有停下来一段事件,才执行一次相应代码。(搜索引擎的联想查询功能,它是在用户进行键入的时候监听keypress事件,然后异步去查询结果。如果用户快速的输入了一连串的字符,假设是10个字符,那么就会在短时间内触发了10次的请求,这无疑不是我们想要的。我们想要的是用户停止输入过了一会(很短,可以设定),才去触发查询的请求 )
区别:防抖和节流本质是不一样的。防抖是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。
function throttle(fn) {
let canRun = true; // 通过闭包保存一个标记
return function () {
if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
canRun = false; // 立即设置为false
setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
canRun = true;
}, 500);
};
}
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi));
函数节流示意代码
函数防抖
function debounce(fn) {
let timeout = null; // 创建一个标记用来存放定时器的返回值
return function () {
clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
fn.apply(this, arguments);
}, 500);
};
}
function sayThis() {
console.log(this);
}
var inputEle = document.getElementById('inp');// <input type="text" id="input">
inputEle.addEventListener('input', debounce(sayThis)); // <input type="text" id="input">
Note:这里面关于this其实有些细节,首先要知道:(1)箭头函数自己没有this,如果箭头函数被非箭头函数包含,this 绑定的就是最近一层非箭头函数的 this。(2)使用ele.addEventListener( 'specificEvent', eventHandler)
给 ele 设置事件监听时,传入 eventHandler
的this
指的就是 ele.
那么inputEle.addEventListener('input', debounce(sayThis))
,给输入元素上的input监听时,设置的处理函数为 debounce(sayThis) 执行返回的匿名函数,该函数中的 this 指向的就是 输入元素,进而可以确定该函数中的箭头函数内的 this。