一、防抖:
使用场景:
为了上面这种防止出现这种情况,就必须好好给他防抖了,不然早晚饿死(因为中午不吃)
。
开玩笑、开玩笑、正题正题。
实际场景,有一个搜索查询的需求,用户在输入框中输入关键字去查询某一条数据,但是由于数据众多,总不能全部返回渲染,一般情况下是返回几十条进行渲染,然后用户输入的时候再去请求服务器查询数据。实现这个功能就要去监听输入框的输入,但是这样做就有一个问题,如果遇到一些搞事的用户(一直按着键盘不放
)就会一直发送请求,然后明天你因为上班敲代码被开除了
,所以这时候就应该拿出防抖,防一下,就像这样
诶,这样不管手这么抖,菜都不会少了,也不会饿死了
原理:当持续触发一个事件时,在n秒内,事件没有再次触发,此时才会执行回调;如果n秒内,又触发了事件,就重新计时。
简单点说就是,你吃饭的时候手一直抖,是夹不到菜,只有不抖的那个时候才能夹到
再简单点就是:我叫你去帮我买可乐,然后你一出门我就叫你回来说我要换成雪碧,然后你再出门我再就你回来说我要换成美年达
,一直这样重复,只有当我最终决定买什么的时候,你才去执行帮我买水这件事
这就是防抖
实现:
function debounce() {
// 定义一个 timer 变量记录定时器并清除
let timer;
// 使用递归是为了确保每一次调用都是一个单独定时器
return function () {
// 如果定时器存在就先清楚
timer && clearTimeout(timer)
// 如果500毫秒内没有再次输入或点击则执行定时器里面的方法
// 否则清除定时器重新定时
timer = setTimeout(()=>{
console.log('500毫秒内只执行最后一次')
},500)
}
}
二、节流:
原理:当一个事件在执行的n秒内或当前状态为false时,不管怎么去触发都不会再次执行该事件,只有在n秒后或当前状态为true时才可再次执行。
简单点就是:我叫你去帮我买可乐,然后你一出门我就叫你回来说我要换成雪碧,然后你再出门我再就你回来说我要换成美年达
,一直这样重复,只有当我最终决定买什么的时候,你才去执行帮我买水这件事
这就是防抖,不管我怎么叫,诶,你就是听不见不回来,只有等你回来才能叫你重新去给我换美年达
实现:
// 方式一:
function throttle(func) {
// 定义一个 timer 变量记录定时器并清除
let timer;
// 记录状态
let flag = true;
// 使用递归是为了确保每一次调用都是一个单独定时器
return function () {
if(flag) {
flag = false
console.log('500毫秒后可以执行下一次')
timer = setTimeout(()=>{
flag = true
timer && clearTimeout(timer)
},500)
}
}
}
// 方式二:
function throttle(diff=500) {
// 当前时间
let now = Date.now();
// 使用递归是为了确保每一次调用都是一个单独定时器
return function () {
const end = Date.now()
if(end - now > diff) {
now = end
console.log('500毫秒后可以执行下一次')
}
}
}
函数防抖:
将几次操作合并为一次操作进行。原理是维护一个定时器,规定在延迟时间后触发函数,只有最后一次操作能被触发。
函数节流:
使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数。
应用场景:
防抖:
1、search联想搜索,用户在不断输入内容的时候,用防抖来节约请求资源。
2、window触发resize时候,不断调整浏览器窗口大小会不断的触发这个事件,用防抖让其只触发一次。
节流:鼠标不断点击(mousedown)触发,让其单位时间内只触发一次。
监听滚动事件,滑到底部自动加载更多。
三、深拷贝:
深拷贝也可以称为深度克隆一个对象,为什么要有深拷贝呢?因为当我们将对象a直接赋值给对象b时,由于对象(数组也是一个对象)是引用数据类型,所以把对象a赋值给对象b时,变量a仅仅是对这个对象的引用,它们指向同一个引用地址,所以在修改a的值时b的值也会发生变化,即:
const a = {
k1: 1,
k2: 2
}
// 将 a 直接赋值给 b ===> 浅拷贝
const b = a
a.k1 = 5
console.log(a) // {k1: 5, k2: 2}
console.log(b) // {k1: 5, k2: 2}
为了解决上面的问题,就可以采用深拷贝进行实现,下面来实现一下
实现:
function deepClone(data) {
// 因为对象里面可能还有对象,所以需要使用到递归,这里就要写递归的退出条件
// 如果对象不存在或者是基本类型,直接返回
if(data === null || typeof data !== 'object') return data
// 如果时日期类型就新建日期并返回
if(data instanceof Date) return new Date(data)
// 如果时日期类型就新建正则并返回
if(data instanceof RegExp) return new RegExp(data)
// 否则,就是对象({} || [] )
// 接下来我们可以使用 Object.prototype.toString 判断是数组还是Object
// 然后创建 对应的变量
// 方式一:
// let cloneObj;
// const toString = Object.prototype.toString
// if(toString .call(data) === '[object Object]') {
// cloneObj = {}
// }else {
// cloneObj = []
// }
// 方式二:
// 通过 对象原型上的 constructor 方法,就能够直接获取当前对象的初始值
let cloneObj = new constructor()
for (const key in data) {
// 为了防止访问到原型上的属性,所以采用 hasOwnPrototype 判断一下
if (Object.hasOwnProperty.call(data, key)) {
const el= object[key];
cloneObj = deepClone(el)
}
}
}
当然了,除了上面的这个方法,还有其他的解决方法,例如:
const a = {
k1: 1,
k2: 2
}
// 将 a 直接赋值给 b ===> 浅拷贝
const b = JSON.parse(JSON.stringify(a))
a.k1 = 5
console.log(a) // {k1: 5, k2: 2}
console.log(b) // {k1: 1, k2: 2}
先通过 a 对象通过 JSON.stringify 将其转换成字符串,因为字符串是基本数据类型,所以可以直接赋值,然后在使用 JSON.parse 将其转成对象,由基本数据类型转成引用数据类型会在内存开辟新的空间,所以他们的引用地址就不一样了,不一样就不会相互影响了。
好了以上就是本期内容了!!!