/**
* 浅比较(如果是对象,只比较第一层属性,与深比较/深拷贝相比性能更好,平常够用)
* @param obj1 任意基本类型或引用类型
* @param obj2 任意基本类型或引用类型
* @returns 是否同一对象
*/
function shallowEqual(obj1: any, obj2: any): boolean {
// 同基本类型,或同引用地址,返回true
if (obj1 === obj2) return true;
// 非对象类型 或 为null 返回 false
if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 == null) return false
const keys1 = Object.keys(obj1)
const keys2 = Object.keys(obj2)
if (keys1.length !== keys2.length) return false
// 如果obj2中没有obj1中的某个属性,或该属性值不相等返回false
for (let key of keys1) {
if (!obj2.hasOwnProperty(key) || obj1[key] !== obj2[key]) {
return false
}
}
return true
}
const obj11 = { a: 1 }
const obj12 = { a: 1 }
console.log(shallowEqual(obj11, obj12)); // true浅拷贝,如果复制的对象是基本数据类型,拷贝的就是值,如果是引用类型,拷贝的就是内存地址,一个对象改变会影响另一个对象
JSON.parse(JSON.stringify()),写法简单,但无法拷贝函数,循环引用,或特殊引用类型.function clone(target) {
if (typeof target !== 'object') return target;
let cloneTarget = {};
for (const key in target) {
cloneTarget[key] = clone(target[key]);
}
return cloneTarget
};/**
* 深拷贝
* @param {Object} target 要拷贝的对象
* @param {WeakMap} map 用于存储循环引用对象的地址
*/
function deepClone(target, map = new WeakMap()) {
if (typeof target !== 'object') return target
if (map.get(target)) {
return map.get(target)
}
const cloneTarget = Array.isArray(target) ? [] : {}
map.set(target, cloneTarget)
for (const key in target) {
if (Object.hasOwnProperty.call(target, key)) {
cloneTarget[key] = deepClone(target[key], map);
}
}
return cloneTarget
}Map 和 WeakMap 的区别
WeakMap是ES6中新增的一种集合类型,叫做弱映射。它和Map是兄弟关系,与Map的区别在于这个弱字,API还是Map那套API。
Map的键可以是任意类型,WeakMap只接受对象作为键,不接受其它类型的值作为键
Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键;WeakMap的键是弱引用,如果创建了一个弱引用对象,不会被垃圾回收关注,如果不再需要,WeakMap 中的键名对象和所对应的键值对会自动消失,不再手动删除引用。
Map可以被遍历,WeakMap不能被遍历
Map, Set 等类型得专门判断
防抖和节流都是防止某一事件频繁触发
施法前摇,在读条期间再次触发会打断施法,重新读条,直到正常读条结束,触发函数。
function debounce(fn, wait) {
let timer;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, wait);
};
}场景: 浏览器窗口resize,文本编辑器自动保存,输入框智能提示
防抖重在清零 clearTimeout(timer)
节流是加锁 限流防止频繁触发 单位时间内只发生一次
function throttle(fn, wait) {
let lastTime = 0;
return function () {
const now = Date.now(); // 获取当前时间戳
if (now - lastTime >= wait) { // 判断当前时间与上次执行时间的差距是否大于等于 wait
fn.apply(this, arguments); // 执行函数
lastTime = now; // 更新最后执行时间
}
};
}function debounce(fn, wait, immediate = false) {
let timer;
return function (...args) {
if(immediate) {
fn.apply(this, args);
immediate = false;
return;
}
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, wait);
};
}const arr = [['a0', 'a1'], ['a1', 'a2'], ['a3']];
// 将含有相同元素的数组合在一起,且去除重复项,输出新arr2
// [ [ 'a0', 'a1', 'a2' ], [ 'a3' ] ]
const mergeArr = (arr) => {
const map = {}; // 用来记录每个元素属于哪个数组
const result = []; // 用来保存合并后的结果
for (const innerArr of arr) {
// 是否有 已存在的arr 含当前arr的重复元素
let existArr = null;
for (const item of innerArr) {
if (map[item]) {
existArr = map[item];
break;
}
}
if (existArr) {
for (const item of innerArr) {
if (map[item]) continue;
existArr.push(item);
map[item] = existArr;
}
} else {
result.push(innerArr);
for (const item of innerArr) {
map[item] = innerArr;
}
}
}
return result;
};
console.log(mergeArr(arr));const root = { a: 1, b: { c: 2, d: { e: 3, }, }, };
// 生成HTML标签Tree
const generateTree = (value) => {
return getTag('root', deepTravelData(value, 0), 0);
};
const deepTravelData = (value, deep) => {
if (!isObject(value)) return getText(value, deep + 1);
const tagArr = [];
for (const key in value) {
if (!Object.hasOwn(value, key)) continue;
tagArr.push(
getTag(
key,
deepTravelData(value[key], deep + 1),
deep + 1
)
);
}
return tagArr.join('\n');
};
const getTag = (tagName, content, deep) => {
const space = ' '.repeat(deep * 4);
return `${space}<${tagName}>\n${content}\n${space}<${tagName}/>`;
};
const getText = (text, deep) => `${' '.repeat(deep * 4)}${text}`;
const isObject = (value) => typeof value === 'object' && value !== null;
console.log(generateTree(root));class LRUCache {
constructor(max) {
this.max = max;
this.store = new Map();
}
put(key, value) {
// 如果已存在,先删除(为了更新顺序)
if (this.store.has(key)) {
this.store.delete(key);
}
this.store.set(key, value);
// 超出容量,删除最久未使用的(Map 的第一个)
if (this.store.size > this.max) {
const oldestKey = this.store.keys().next();
this.store.delete(oldestKey);
}
}
get(key) {
if (!this.store.has(key)) return -1;
const value = this.store.get(key);
// 访问即更新使用顺序
this.store.delete(key);
this.store.set(key, value);
return value;
}
}
const cache = new LRUCache(2); // 容量为 2
cache.put(1, 1); // 缓存是 {1=1}
cache.put(2, 2); // 缓存是 {1=1, 2=2}
console.log(cache.get(1)); // 返回 1,缓存是 {2=2, 1=1}
cache.put(3, 3); // 删除键 2(因为最久未使用),缓存是 {1=1, 3=3}
console.log(cache.get(2)); // 返回 -1(未找到)
cache.put(4, 4); // 删除键 1,缓存是 {3=3, 4=4}
console.log(cache.get(1)); // 返回 -1(未找到)
console.log(cache.get(3)); // 返回 3
console.log(cache.get(4)); // 返回 4var arr = [["1","2"],["3","4","5"]];
// 预期结果
// [["1","3"],["1","4"],["1","5"],["2","3"],["2","4"],["2","5"]]
const combile = (arr) => {
for(let i = 0; i < arr.length; i++) {
}
}var obj = [
{ id:3, parent:2 },
{ id:1, parent:null },
{ id:2, parent:1 }
]
// o = {
// id: 1,
// parent: null,
// children: [{
// id: 2,
// parent: 1,
// children: [{
// id: 3,
// parent: 2
// }]
// }]
// };实现一个 EventBus,支持订阅、取消订阅、发布事件,以及一次性订阅(once)