JS高级—19—防抖、节流、深拷贝、数组去重、事件总线;
一、防抖和节流
function debounce(time) { let timer return function () { if (timer) clearTimeout(timer) timer = setTimeout(() => { console.log('helloWorld') }, time) } }
function once(func) {
let flag = false
return function () {
if (!flag) {
flag = true
func()
}
}
}
function throttle(fn, interval, { immediate = true }) { let initDate = 0; return function () { let date = new Date().getTime(); if (initDate === 0 && !immediate) initDate = date; if (date - initDate >= interval) { fn(); initDate = date; } }; }
防抖:不断的对要触发的行为做延迟;
节流:限制行为次数;按照固定频率触发;
总的来说:都是对用户行为
点击事件后,等待超过单位时间才会执行回调函数;
点击事件后,等待一定单位时间(等待期间不管点击多少次都抛弃),触发一次;
二、深拷贝
简单的数据,我们可以使用
JSON.parse(JSON.stringify(obj)来进行,
如果有复杂数据肯定不行,因为JSON对undefined、symbol键、symbol值、function、set、map都不能进行处理;
所以,我们还是要写一个自己的深拷贝函数;
// const weakMap = new WeakMap() //解决对象的循环引用; export function deepClone(originValue, weakMap = new WeakMap()) { //使用在函数参数中定义weakmap,而不在全局中定义weakmap,这样可以避免每次调用deepclone时都往weakmap里插入数据,导致后期的weakmap将会成为一个非常的数据; const toString = Object.prototype.toString; // console.log(toString.call(originValue), originValue) //1.传进来一个基本数据类型 if (toString.call(originValue) === '[object String]') return originValue; if (toString.call(originValue) === '[object Null]') return originValue; if (toString.call(originValue) === '[object Undefined]') return originValue; if (toString.call(originValue) === '[object Boolean]') return originValue; if (toString.call(originValue) === '[object Number]') return originValue; if (toString.call(originValue) === '[object Symbol]') return originValue; //2.引用数据类型object if (toString.call(originValue) === '[object Object]') { const newObj = {}; //2.0循环引用问题 if (weakMap.has(originValue)) return weakMap.get(originValue); //如果map中已经有了这个引用,则直接返回map里的这个值即可; weakMap.set(originValue, newObj); //2.1普通的key for (const key in originValue) { newObj[key] = deepClone(originValue[key], weakMap); } //2.2对Symbol的key进行特殊的处理 const symbolKeys = Object.getOwnPropertySymbols(originValue); for (const sKey of symbolKeys) { // const newSKey = Symbol(sKey.description) newObj[sKey] = deepClone(originValue[sKey]); } return newObj; } //3.引用数据类型array if (toString.call(originValue) === '[object Array]') { return [...originValue]; } //4.引用数据类型function if (toString.call(originValue) === '[object Function]') { //函数直接返回即可,不需要新建一个函数然后把所有的属性和方法都在添加一遍 return originValue; } //5.引用数据类型set if (toString.call(originValue) === '[object Set]') { return new Set([...originValue]); } //6.引用数据类型map if (toString.call(originValue) === '[object Map]') { console.log(...originValue); return new Map([...originValue]); } //6.other }
三、数组去重
第一个需求:数组里元素去重,遇到对象,如果对象所有属性一致则对象判定为重复的需要去重
第二个需求:数组里元素去重,遇到对象,如果对象的id一致就对象判定为重复的需要去重
const arr = [1, 1, null, null, undefined, undefined, 'kobe', 'kobe', { id: 1, age: { name: 18 } }, { id: 1, age: { name: 18 } }, { age: 1, age: { name: 20 } }, [1, 2, 3], [1, 2, 3], true, true]; //1.需求:数组里元素去重,遇到对象,如果对象所有属性一致则对象判定为重复的需要去重; //1.1使用set,基本数据类型都会被去重,但是引用数据类型不可去重(因为引用数据类型虽然内容一样,但是堆地址不一样故set认为不一样); function removeDuplicate(oldArr) { console.log([...new Set(oldArr)]); return [...new Set(oldArr)]; } [1,null,undefined,'kobe',true { id: 1, age: { name: 18 } }, { id: 1, age: { name: 18 } }, { age: { name: 20 } }, [ 1, 2, 3 ], [ 1, 2, 3 ], ] //1.2引用类型也去重 function removeDuplicate(oldArr) { const newArr = oldArr.map((item) => JSON.stringify(item)); console.log([...new Set(newArr)]); return [...new Set(newArr)].map((item) => { if (item === undefined) return undefined; //edge-case:undefind无法Json.parse; return JSON.parse(item); }); } [ 1,null,undefined,'kobe',true, { id: 1, age: { name: 18 } }, { age: { name: 20 } }, [ 1, 2, 3 ], ]
const arr = [1, 1, null, null, undefined, undefined, 'kobe', 'kobe', { id: 1, age: { name: 18 } }, { id: 1, age: { name: 18 } }, { age: 1, age: { name: 20 } }, [1, 2, 3], [1, 2, 3], true, true]; //2.需求:数组里元素去重,遇到对象,如果对象id一致则对象判定为重复的需要去重; /** * 数组的元素有很多不同的类型,不同的类型使用不同的去重办法; * 去重前:将基本数据类型、引用数据类型之数组、引用数据类型之对象,都放到不同的数组里;baseOfArr,arrOfArr,objOfArr * 去重时: * 去重后:数组合并为新数组,最后返回; */ function removeDuplicate(oldArr) { let baseOfArr = [], arrOfArr = []; const objOfArr = [], obj = {}, toString = Object.prototype.toString, baseDataType = ['[object Null]', '[object Undefined]', '[object Boolean]', '[object Number]', '[object Symbol]', '[object String]']; for (let i = 0; i < oldArr.length; i++) { // 如果是基本数据类型 if (baseDataType.includes(toString.call(oldArr[i]))) baseOfArr.push(oldArr[i]); // 如果是引用数据类型之数组 if (toString.call(oldArr[i]) === '[object Array]') arrOfArr.push(oldArr[i]); // 如果是引用数据类型之对象 if (toString.call(oldArr[i]) === '[object Object]') { if (!obj[oldArr[i].id]) { objOfArr.push(oldArr[i]); //obj['id'] = undefined; obj[oldArr[i].id] = true; //obj['id'] = true; } } // other case continue; } baseOfArr = [...new Set(baseOfArr)]; arrOfArr = [...arrOfArr.map((item) => JSON.stringify(item))].map((item) => JSON.parse(item)); // console.log(baseOfArr, arrOfArr, objOfArr); return [...baseOfArr, ...arrOfArr, ...objOfArr]; } console.log(removeDuplicate(arr));
[1,null,undefined,'kobe',true,
[ 1, 2, 3 ],
[ 1, 2, 3 ],
{ id: 1, age: { name: 18 } },
{ age: { name: 20 } }
]
//1.obj改为用uniqIdArr.inclued()判断;
//2.if上用Json.stringify()提前判断,可以直接过滤出单个元素,然后直接加到数组里就是可以用的;不用在最后再给数据类型为数组的元素再去重一遍;
function removeDuplicate3(arr) {
const uniqArr = [];
const uniqIdArr = [];
arr.forEach((a) => {
if (Object.prototype.toString.call(a) === '[object Object]') {
if (!uniqIdArr.includes(a.id)) {
uniqArr.push(a);
uniqIdArr.push(a.id);
}
} else {
if (!uniqArr.some((ua) => JSON.stringify(ua) === JSON.stringify(a))) {
uniqArr.push(a);
}
}
});
return uniqArr;
}
四、事件总线
完整的可以看老师的github之hy-event-bus仓库;