1. 防抖
防抖与节流通常作为项目优化的手段,一般都是为了防止用户在短时间内快而频地多次操作,触发动作执行。比如防止用户点击多次提交按钮,触发表单多次提交;防止用户拉动滚动条,多次触发加载更多等情况。
防抖:为了防止快速且频繁的触发事件而导致多次执行事件函数,我们这多次触发的事件只执行一次事件函数。
我们可以设定一个间隔时间,如果两次事件触发的间隔时间低于设定的时间,则定义为频繁。
debounce的特点是当事件快速连续不断触发时,动作只会执行一次。分为两种,一种是延迟debounce,一种是前缘debounce。
- 延迟debounce,是在周期结束时执行,策略是当事件被触发时,设定一个周期延迟执行动作,若期间又被触发,则重新设定周期,直到周期结束,执行动作。
- 前缘debounce,是在周期开始时执行,即执行动作在前,然后设定周期,周期内有事件被触发,不执行动作,且周期重新设定。
应用场景:
1.登录、发短信等按钮避免用户点击太快 以至于发送了多次请求 需要防抖
2.调整浏览器窗口大小时 resize次数过于频繁 造成计算过多
3.搜索框输入时,不需要每次输入一个字的时候就进行搜索 而是等待没有再次输入时返回结果
const debounce=(()=>{
let timer = null // 用来接受定时器
return (callback, time = 800)=>{
timer && clearTimeout(timer) // 如果两秒内再触发,会清除timer(设为2000),再等待2秒执行callback
timer = setTimeout(callback, time)
}
})() // 闭包
debounce(()=>{
// 需要执行的操作
console.log('----')
}, 2000)
2.节流
防抖动是多次触发但只会执行一次,节流是多次触发但周期内只会执行一次。
throttling,节流的策略是,每个时间周期内,不论触发多少次事件,也只执行一次动作。上一个时间周期结束后,又有事件触发,开始新的时间周期,同样新的时间周期也只会执行一次动作。 节流策略也分前缘和延迟两种。与debounce类似,延迟是指周期结束后执行动作,前缘是指执行动作后再开始周期。
应用场景:
1.scroll事件 每隔一秒计算一次位置信息等
2.input框实时搜索并发送请求展示下拉列表 每隔一秒发送一次请求 (也可做防抖)
// 节流 减少一段时间的触发频率 2s发送一次 上一次触发事件的时间 当前触发事件的时间
// 08:10 ~ 08:13 > 2s 发送请求
const throttle = (()=>{
let last = 0 // 接收上次触发的时间
return (callback, time = 800) =>{
let now = +new Date() // 拿到当前的时间戳 +是为了转化成Number类型
if(now - lase > time){
callback()
last = now
}
}
})()
throttle(()=>{
// 需要执行的操作
console.log('----')
}, 2000)
防抖与节流的注意事项:
①使用闭包
为了避免全局的命名污染,因此我们不考虑使用全局变量。同时为了让所需变量能得到缓存,因此我们使用闭包存储部分需要用到的变量
②this指向的问题以及event参数:
因为事件触发调用的,是防抖/节流函数return出来的函数,而不是事件函数func,事件函数func是作为参数传入防抖/节流函数,其this指向是window,而不是触发事件调用函数的dom,同理,自然也拿不到事件自带的event参数,所以我们要通过闭包,存储this上下文以及argument这一传递给函数的参数的类数组对象
3.大小写转换
type为1时,将字符串转化为大写;
type为2时,将字符串转化为小写;
type为3时,字符串的首字母大写 其余小写。
const trunCase = (str, type) =>{
switch(type){
case 1:
return str.toUpperCase()
case 2:
return str.toLowerCase()
case 3:
return str[0].toUpperCase() + str.substr(1).toLowerCase()
default:
return str
}
}
4.数组对象根据字段去重
const uniqueArrayObject = (arr=[], key='id')=>{
if(arr.length === 0) return ;
let list = [] // 接收去重之后的数组
const map = {}
arr.forEach(item =>{
if(!map[item[key]]){ // 第二次把属性值为1作为map的属性放在里面进行判断
map[item[key]] = item
}
})
list = Object.values(map)
return list
}
5.校验数据类型
const typeOf = (obj) =>{
return Object.prototype.toString.call(obj).slice(8, -1)
// [object Array]=>Array [object Number] =>Number
}
typeOf([1,2])
6.滚动到页面顶部
const scrollTop = ()=>{
// 获取滚动条的高度
const height = document.documentElement.scrollTop || document.body.scrollTop
if(height > 10){
window.requestAnimationFrame(scrollTop) // 缓冲效果 浏览器页面每一次重绘的时候,就会调用这个方法
window.scrollTo(0, height-height/8) // 将内容滚动到指定坐标
}
}
7.滚动到对应元素的位置
const scrollElement = (element)=>{
// scrollIntoView 将调用的元素 滚动到浏览器的可见区域
document.querSelector(element).scrollIntoView({behavior: 'smooth'}) // smoth 平滑滚动
}
8.获取当前时间
function getTime(date=new Date()){
let y = date.getFullYear() // 获取年份
let m = date.getMonth() + 1 // 获取月份
m = m <10 ? '0' + m : m
let d = date.getDate() + 1 // 获取日期
d = d < 10 ? '0' + d : d
return y + '/' + m + '/' + d
}
9.获取当月的第一天和最后一天
function getFirstLastDay(){
let now = new Date()
let y = now.getFullYear()
let m = now.getMonth()
let firstDay = new Date(y, m, 1) // 本月开始的时间
let lastDay = new Date(y, m + 1, 0) // 本月最后一天的时间 下个月的倒数一天
firstDay = firstDay.getMonth() + 1 + '/' + '0' + firstDay.getDate()
lastDay = lastDay.getMonth() + 1 + '/' + lastDay.getDate()
return {firstDay, lastDay}
}
10.模糊查询
list = [
{id:1, name:'张三'},
{id:2, name:'李四'},
{id:3, name:'王五'},
{id:2, name:'ww'}
]
const searchQuery = (list, keyword, attr='name') =>{
const reg = new RegExp(keyword)
const arr = [] // 用于接收匹配到的内容
list.forEach(item=>{
if(reg.test(item[attr])){ // test:一个在字符串中测试是否匹配的 RegExp 方法,它返回 true 或 false
arr.push(item)
}
})
return arr
}