通常事件触发之后,会立即执行相对应的函数,而防抖就是,事件触发之后,过一段时间才会触发相应的函数
事件不断的触发,执行函数会无限制的延后
let inputElement = document.getElementById("input");
inputElement.oninput = zcdebounce(function () {
console.log(this.value);
}, 1000);
function zcdebounce(fn, delay) {
//接收要执行的函数,以及延迟时间
//定义延迟定时器timer
let timer = null;
//这里需要定义一个新函数,用于作为返回值
const _debounce = () => {
//判断timer是否存在,有的话就清除
if (timer) clearTimeout(timer);
//通过延迟时间,来确定要执行的时机
timer = setTimeout(() => {
fn();
//执行完应当执行的函数,应该将timer置为null
timer = null;
}, delay);
};
return _debounce;
}
let inputElement = document.getElementById("input");
inputElement.oninput = zcdebounce(function () {
console.log(this.value);
}, 1000);
function zcdebounce(fn, delay) {
//接收要执行的函数,以及延迟时间
//定义延迟定时器timer
let timer = null;
//这里需要定义一个新函数,用于作为返回值
const _debounce = function(){
//判断timer是否存在,有的话就清除
if (timer) clearTimeout(timer);
//通过延迟时间,来确定要执行的时机
timer = setTimeout(() => {
fn.apply(this);
//执行完应当执行的函数,应该将timer置为null
timer = null;
}, delay);
};
return _debounce;
}
let inputElement = document.getElementById("input");
let count = 1;
inputElement.oninput = zcdebounce(function (event) {
console.log(this.value, event);
}, 1000);
function zcdebounce(fn, delay) {
//接收要执行的函数,以及延迟时间
//定义延迟定时器timer
let timer = null;
//这里需要定义一个新函数,用于作为返回值
const _debounce = function (...arg) {
console.log(arg);
//判断timer是否存在,有的话就清除
if (timer) clearTimeout(timer);
//通过延迟时间,来确定要执行的时机
timer = setTimeout(() => {
fn.apply(this, arg);
//执行完应当执行的函数,应该将timer置为null
timer = null;
}, delay);
};
return _debounce;
}
let inputElement = document.getElementById("input");
let cancleBtn = document.getElementById("btn");
//将防抖工具函数的返回值返回
const debounceFn = zcdebounce(function () {
console.log(this.value, event);
}, 1000);
//对输入框使用防抖函数
inputElement.oninput = debounceFn;
//对取消按钮,增加事件,该事件用于取消防抖函数的执行
cancleBtn.onclick = function () {
debounceFn.cancle();
};
function zcdebounce(fn, delay) {
//接收要执行的函数,以及延迟时间
//定义延迟定时器timer
let timer = null;
//这里需要定义一个新函数,用于作为返回值
const _debounce = function (...arg) {
//判断timer是否存在,有的话就清除
if (timer) clearTimeout(timer);
//通过延迟时间,来确定要执行的时机
timer = setTimeout(() => {
fn.apply(this, arg);
//执行完应当执行的函数,应该将timer置为null
timer = null;
}, delay);
//给_debounce增加一个属性
_debounce.cancle = function () {
if (timer) clearTimeout(timer);
};
};
return _debounce;
}
增加一个立即执行的功能:即当输入第一个字母或者单词的时候,不会等待,会立即执行,之后才会利用防抖机制
比如,我们输入macbook,当输入第一个m的时候,就会立即执行一次函数,等待输入完成acbook再次执行
注意这里在设计的时候有一个原则:一个函数最好只做一件事,一个变量只存储一种类别的状态;
若我们用immediate既作为是否立即执行,又作为立即执行是否完成,会对代码造成逻辑错误,因此引入了变量isDone
同时,对于用户传进来的变量,我们最好不要进行更改
let inputElement = document.getElementById("input");
let cancleBtn = document.getElementById("btn");
//将防抖工具函数的返回值返回
const debounceFn = zcdebounce(function () {
console.log(this.value, event);
}, 1000);
//对输入框使用防抖函数
inputElement.oninput = debounceFn;
//对取消按钮,增加事件,该事件用于取消防抖函数的执行
cancleBtn.onclick = function () {
debounceFn.cancle();
};
function zcdebounce(fn, delay, immediate = true) {
//接收要执行的函数,以及延迟时间
//定义延迟定时器timer
let timer = null;
let isDone = false;
//这里需要定义一个新函数,用于作为返回值
const _debounce = function (...arg) {
//如果是立即执行,则直接执行函数,同时return
if (immediate && !isDone) {
fn.apply(this, arg);
isDone = true;
return;
}
//判断timer是否存在,有的话就清除
if (timer) clearTimeout(timer);
//通过延迟时间,来确定要执行的时机
timer = setTimeout(() => {
fn.apply(this, arg);
//将是否完成当次防抖,设置为flase
isDone = false;
//执行完应当执行的函数,应该将timer置为null
timer = null;
}, delay);
};
//给_debounce增加一个属性
_debounce.cancle = function () {
if (timer) {
clearTimeout(timer);
//将是否完成当次防抖,设置为flase
isDone = false;
//执行完应当执行的函数,应该将timer置为null
timer = null;
}
};
return _debounce;
}
在某些场景下,我们需要获取函数的返回值,那么这个返回值应当怎么进行获取
const debounceFn = zcdebounce(function () {
console.log(this.value, event);
return "zhangcheng"
}, 1000);
//我们主动执行这个函数,实际内部执行的是_debounce函数,因此不能获得return "zhangcheng"的返回值
debounceFn()
//将防抖工具函数的返回值返回
const debounceFn = zcdebounce(
function () {
return "zhangcheng";
},
1000,
true,
function (res) {
console.log(res);
}
);
debounceFn();
function zcdebounce(fn, delay, immediate = true, resCallBack) {
//接收要执行的函数,以及延迟时间
//定义延迟定时器timer
let timer = null;
let isDone = false;
let res = undefined;
//这里需要定义一个新函数,用于作为返回值
const _debounce = function (...arg) {
//如果是立即执行,则直接执行函数,同时return
if (immediate && !isDone) {
res = fn.apply(this, arg);
if (resCallBack) resCallBack(res);
isDone = true;
return;
}
//判断timer是否存在,有的话就清除
if (timer) clearTimeout(timer);
//通过延迟时间,来确定要执行的时机
timer = setTimeout(() => {
res = fn.apply(this, arg);
if (resCallBack) resCallBack(res);
//将是否完成当次防抖,设置为flase
isDone = false;
//执行完应当执行的函数,应该将timer置为null
timer = null;
}, delay);
};
//给_debounce增加一个属性
_debounce.cancle = function () {
if (timer) {
clearTimeout(timer);
//将是否完成当次防抖,设置为flase
isDone = false;
//执行完应当执行的函数,应该将timer置为null
timer = null;
}
};
return _debounce;
}
//将防抖工具函数的返回值返回
const debounceFn = zcdebounce(
function () {
return "zhangcheng";
},
1000,
true
);
debounceFn().then((res) => {
console.log(res);
});
function zcdebounce(fn, delay, immediate = true, resCallBack) {
//接收要执行的函数,以及延迟时间
//定义延迟定时器timer
let timer = null;
let isDone = false;
let res = undefined;
//这里需要定义一个新函数,用于作为返回值
const _debounce = function (...arg) {
return new Promise((resolve, reject) => {
//如果是立即执行,则直接执行函数,同时return
if (immediate && !isDone) {
res = fn.apply(this, arg);
if (resCallBack) resCallBack(res);
resolve(res);
isDone = true;
return;
}
//判断timer是否存在,有的话就清除
if (timer) clearTimeout(timer);
//通过延迟时间,来确定要执行的时机
timer = setTimeout(() => {
res = fn.apply(this, arg);
if (resCallBack) resCallBack(res);
resolve(res);
//将是否完成当次防抖,设置为flase
isDone = false;
//执行完应当执行的函数,应该将timer置为null
timer = null;
}, delay);
//给_debounce增加一个属性
_debounce.cancle = function () {
if (timer) {
clearTimeout(timer);
//将是否完成当次防抖,设置为flase
isDone = false;
//执行完应当执行的函数,应该将timer置为null
timer = null;
}
};
});
};
return _debounce;
}
函数的执行,会按照固定的频率来执行
可以看做游戏中,角色的攻击速度,角色的发出去的攻击,不会按照玩家点击的速度发射子弹,而是又固定发射子弹的频率
inputElement.oninput = zcthrottle(function () {
console.log(123);
}, 1000);
function zcthrottle(fn, interval) {
let timer = null;
const _throttle = function () {
if (timer) return;
timer = setTimeout(function () {
fn();
timer = null;
}, interval);
};
return _throttle;
}
inputElement.oninput = zcthrottle(function () {
console.log(123);
}, 1000);
function zcthrottle(fn, interval) {
let startTime = 0;
const _throttle = function () {
const currentTime = new Date().getTime();
const resTime = interval - (currentTime - startTime);
if (resTime <= 0) {
fn();
startTime = currentTime;
}
};
return _throttle;
}
与防抖函数的原理一致
inputElement.oninput = zcthrottle(function (event) {
console.log(this, event);
}, 1000);
function zcthrottle(fn, interval) {
let startTime = 0;
const _throttle = function (...args) {
const currentTime = new Date().getTime();
const resTime = interval - (currentTime - startTime);
if (resTime <= 0) {
fn.apply(this, args);
startTime = currentTime;
}
};
return _throttle;
}
通过以上代码可以发现,节流函数默认就是立即执行的,但是想设置为第一次不立即执行,应当怎么操作
inputElement.oninput = zcthrottle(
function (event) {
console.log(this, event);
},
2000,
false
);
function zcthrottle(fn, interval, leading = true) {
let startTime = 0;
const _throttle = function (...args) {
const currentTime = new Date().getTime();
//立即执行的控制
//默认情况下是立即执行的,所以只需要考虑不立即执行的情况
//若只有leading进行控制,且leading为false的时候,则函数不会执行,因此引入startTime
if (!leading && startTime === 0) {
startTime = currentTime;
}
const resTime = interval - (currentTime - startTime);
if (resTime <= 0) {
fn.apply(this, args);
startTime = currentTime;
}
};
return _throttle;
}
网上的一些文章,对于深拷贝和浅拷贝的概念会有些混淆,深拷贝和浅拷贝都会创建新的引用类型,不同的是,对于引用类型中包含引用类型的数据处理不同
let info = {
a: 100,
b: "200",
c: {
d: 300,
},
};
//引用赋值
let info1 = info;
//浅拷贝
let info2 = { ...info };
//深拷贝
//通过JSON方式实现的深拷贝,对于引用类型中存在函数的,symbol类型的,会默认删除,无法实现拷贝
let info3 = JSON.parse(JSON.stringify(info));
function isObject(originValue){
let valueType = typeof originValue
return originValue != null && (valueType === object || valueType === function)
}
function deepCopy(originValue) {
//应当对传入的参数进行判断,为普通类型,null均应该直接返回
if (!isObject(originValue)) return originValue;
// 若传入的为对象类型,则创建一个新的对象
let newObj = {};
//对传入的对象进行遍历
for (const key in originValue) {
//应当对传入对象的value进行递归操作
newObj[key] = deepCopy(originValue[key]);
}
return newObj;
}
function deepCopy(originValue) {
//应当对传入的参数进行判断,为普通类型,null均应该直接返回
if (!isObject(originValue)) return originValue;
// 判断传入参数的类型,是对象还是数组
//对象与数组的判断,这只是其中一种方法
let newObj = Array.isArray(originValue) ? [] : {};
//对传入的对象进行遍历
for (const key in originValue) {
//应当对传入对象的value进行递归操作
newObj[key] = deepCopy(originValue[key]);
}
return newObj;
}
set类型、函数类型、值和key是Symbol类型等
function deepCopy(originValue) {
//应当对传入的参数进行判断,为普通类型,null均应该直接返回
if (!isObject(originValue)) return originValue;
//对set类型的数据进行判断
if (originValue instanceof Set) {
let newSet = new Set();
//对传进来的set数据进行遍历
for (const item of originValue) {
//为了防止set中有对象的存在,所以再次使用递归
newSet.add(deepCopy(item));
}
return newSet;
}
// 判断传入参数的类型,是对象还是数组
//对象与数组的判断,这只是其中一种方法
let newObj = Array.isArray(originValue) ? [] : {};
//对传入的对象进行遍历
for (const key in originValue) {
//应当对传入对象的value进行递归操作
newObj[key] = deepCopy(originValue[key]);
}
return newObj;
}
function deepCopy(originValue) {
//应当对传入的参数进行判断,为普通类型,null均应该直接返回
if (!isObject(originValue)) return originValue;
//对set类型的数据进行判断
if (originValue instanceof Set) {
let newSet = new Set();
//对传进来的set数据进行遍历
for (const item of originValue) {
//为了防止set中有对象的存在,所以再次使用递归
newSet.add(deepCopy(item));
}
return newSet;
}
//如果传入的参数是函数类型,则直接return出去
if (typeof originValue === "function") {
return originValue;
}
// 判断传入参数的类型,是对象还是数组
//对象与数组的判断,这只是其中一种方法
let newObj = Array.isArray(originValue) ? [] : {};
//对传入的对象进行遍历
for (const key in originValue) {
//应当对传入对象的value进行递归操作
newObj[key] = deepCopy(originValue[key]);
}
return newObj;
}
function deepCopy(originValue) {
//若值是一个Symbol类型,则返回一个Symbol
if (typeof originValue === "symbol") {
return Symbol();
}
//应当对传入的参数进行判断,为普通类型,null均应该直接返回
if (!isObject(originValue)) return originValue;
//对set类型的数据进行判断
if (originValue instanceof Set) {
let newSet = new Set();
//对传进来的set数据进行遍历
for (const item of originValue) {
//为了防止set中有对象的存在,所以再次使用递归
newSet.add(deepCopy(item));
}
return newSet;
}
//如果传入的参数是函数类型,则直接return出去
if (typeof originValue === "function") {
return originValue;
}
// 判断传入参数的类型,是对象还是数组
//对象与数组的判断,这只是其中一种方法
let newObj = Array.isArray(originValue) ? [] : {};
//对传入的对象进行遍历
for (const key in originValue) {
//应当对传入对象的value进行递归操作
newObj[key] = deepCopy(originValue[key]);
}
return newObj;
}
function deepCopy(originValue) {
//若值是一个Symbol类型,则返回一个Symbol
if (typeof originValue === "symbol") {
return Symbol(originValue.description);
}
//应当对传入的参数进行判断,为普通类型,null均应该直接返回
if (!isObject(originValue)) return originValue;
//对set类型的数据进行判断
if (originValue instanceof Set) {
let newSet = new Set();
//对传进来的set数据进行遍历
for (const item of originValue) {
//为了防止set中有对象的存在,所以再次使用递归
newSet.add(deepCopy(item));
}
return newSet;
}
//如果传入的参数是函数类型,则直接return出去
if (typeof originValue === "function") {
return originValue;
}
// 判断传入参数的类型,是对象还是数组
//对象与数组的判断,这只是其中一种方法
let newObj = Array.isArray(originValue) ? [] : {};
//对传入的对象进行遍历
for (const key in originValue) {
//应当对传入对象的value进行递归操作
newObj[key] = deepCopy(originValue[key]);
}
//当key是Symbol的时候,要单独获取其key
let symbolKeys = Object.getOwnPropertySymbols(originValue);
for (const item of symbolKeys) {
newObj[Symbol(item.description)] = deepCopy(originValue[item]);
}
return newObj;
}
做跨文件、跨组件的操作
//我们要将env中的数据,传递给main中去
--------env.vue
zcEventBus.emit("envClick","zhangcheng",198)
--------main.vue
zcEventBus.on("envClick",function(name,age){
console.log("监听到了")
})
class zcEventBus {
constructor() {
this.obj = {};
}
//on事件需要接收两个参数,事件名称,回调函数
on(eventName, callBackFn) {
//采用对象的结构,一个事件名称,后面跟着数组,可以包含多个事件
//{eventName:[fn1,fn2]}
//但是第一次执行的时候,this.obj[eventName]不是一个数组,需要进行判断
let eventArr = this.obj[eventName];
//第一次肯定是undefined,当!eventArr的时候,为true
if (!eventArr) {
//将this.obj[eventName]初始化数组
eventArr = [];
this.obj[eventName] = eventArr;
}
//相当于在this.obj[eventName]中push了事件
eventArr.push(callBackFn);
}
//emit事件需要接收两个参数,事件名称,以及参数
emit(eventName, ...arg) {
console.log(eventName);
//需要遍历eventName对用的事件数组,并依次执行
//首先判断是否存在,不存在直接返回
let eventArr = this.obj[eventName];
if (!eventArr) return;
//若存在,则直接遍历执行
for (const fn of eventArr) {
fn(...arg);
}
}
}