# 定时器
WARNING
setInterval 函数的用法与 setTimeout 完全一致,区别仅仅在于 setInterval 指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。
所以,就我就自己以 setTimout 为例即可
某个函数或某段代码,在多少毫秒(默认为 0)后执行。除此之外,还会返回一个整数(表示定时器编号),可以用来取消定时器
var timerId = setTimeout(func|code, delay);
# 1、常规使用
- 使用
// 1、某段代码
setTimeout('console.log(2)', 1000)
// 2、某个函数
setTimeout(function () {
console.log(2)
}, 1000)
- 取消
var id1 = setTimeout(f, 1000)
var id2 = setInterval(f, 1000)
clearTimeout(id1)
clearInterval(id2)
# 2、更多内容
# ① 传参
setTimeout(
function (a, b) {
console.log(a + b)
},
1000,
1,
1
)
# ② 执行顺序 ✨
根据其运行机制:setTimeout
和 setInterval
指定的回调函数,必须等到本轮事件循环的所有同步任务都执行完,才会开始执行。
知识回顾
DOM 事件流:事件冒泡机制?
我们知道用户自定义的回调函数,通常在浏览器的默认动作之前触发。
例如:用户在输入框输入文本,keypress 事件会在浏览器接收文本之前触发,但我们想要的是 keypress 事件处理的是浏览器接收的文本。
document.getElementById('input-box').onkeypress = function () {
var self = this
// 推迟即可
setTimeout(function () {
self.value = self.value.toUpperCase()
}, 0)
}
又例如:我们可以将那些计算量大、耗时长的任务,放到setTimeout(f, 0)
里面执行。
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)
# ③ this 指向
我们知道 setTimeout
使得方法内部的 this
关键字指向全局环境,而不是定义时所在的那个对象。
- 闭包解决
var x = 1
var obj = {
x: 2,
y: function () {
console.log(this.x)
}
}
setTimeout(function () {
// 函数套函数✨
obj.y()
}, 1000)
// 2
- 函数方法解决
var x = 1
var obj = {
x: 2,
y: function () {
console.log(this.x)
}
}
// 直接手动改变this指向✨
setTimeout(obj.y.bind(obj), 1000)
// 2
# debounce / throttle 函数
本质上来讲,使用防抖、节流都与一个目标相关:当多次执行某一动作,进行函数调用次数的限制,节省资源
# 一、防抖
在事件触发 n 秒后执行函数,如果在 n 秒内再次出发,重新计时
理解
王者荣耀英雄回城效果。
// 使用示例
window.onscroll = function () {
let testThrottleFn = debounce(function () {
console.log('发起ajax请求')
}, 3000)
testThrottleFn()
}
# 应用场景
登录、发短信等
按钮
避免用户点击太快,以致于发送了多次请求在搜索框搜索输入。只需用户最后一次输入完,再发送请求
手机号、邮箱验证
输入检测
(change、input、blur、keyup 等事件触发,每次键入都会触发)窗口大小
Resize
。只需窗口调整完成后,计算窗口大小。防止重复渲染。鼠标的 mousemove、mouseover 导航条上,用户不停的在导航区域滑动相当于
# 1、简单版本
var timer //全局的timer,只有一个
function debounce(fn, delay) {
if (timer) {
clearTimeout(timer) //保证只开启一个定时器
}
timer = setTimeout(function () {
fn() //延迟delay,执行函数
}, delay)
}
# 2、闭包版本
function debounce(fn, wait) {
let timeout
return function (...arguments) {
let context = this
let args = arguments
if (timeout) {
clearTimeout(timeout)
}
timeout = setTimeout(function () {
fn.apply(context, args)
}, wait)
}
}
# 3、立即执行版本
function debounce(func, wait, immediate) {
let timeout
return function (...arguments) {
let context = this
let args = arguments
if (timeout) {
clearTimeout(timeout)
}
if (immediate) {
let callNow = !timeout // 第一次会立即执行,以后只有事件执行后才会再次触发
timeout = setTimeout(function () {
timeout = null
}, wait)
if (callNow) {
func.apply(context, args)
}
} else {
timeout = setTimeout(function () {
func.apply(context, args)
}, wait)
}
}
}
# 二、节流
当多次执行某一动作,每隔一段时间,只执行一次函数。
理解
王者荣耀英雄平 a 效果
# 应用场景
滚动加载
,加载更多或滚到底部监听,window.onscroll 和滑到底部自动加载更多谷歌搜索框,
搜索联想
功能
# 1、定时器版本
// 细节1:默认延迟时间设置
function throttle(fn, delay = 500) {
let timer = null
return function (...args) {
if (timer) {
return
}
// 细节2:使用箭头函数(this问题)
// 指定时间后触发
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, delay)
}
}
window.onscroll = function () {
let testThrottleFn = throttle(function () {
console.log('发起ajax请求')
}, 3000)
testThrottleFn()
}
# 2、时间戳版本
function throttle(fn, delay) {
let oldTime = Date.now()
return function (...args) {
let newTime = Date.now()
// 比较时间间隔
if (newTime - oldTime >= delay) {
fn.apply(null, args)
oldTime = Date.now()
}
}
}
window.onscroll = function () {
let testThrottleFn = throttle(function () {
console.log('发起ajax请求')
}, 3000)
testThrottleFn()
}
# 3、高级写法
function throttled(fn, delay) {
let timer = null
let starttime = Date.now()
return function () {
let curTime = Date.now() // 当前时间
let remaining = delay - (curTime - starttime) // 从上一次到现在,还剩下多少多余时间
let context = this
let args = arguments
clearTimeout(timer)
if (remaining <= 0) {
fn.apply(context, args)
starttime = Date.now()
} else {
timer = setTimeout(fn, remaining)
}
}
}
深浅拷贝 →