浅拷贝和深拷贝
- 浅拷贝(Shallow Copy):浅拷贝创建了一个新的对象,但它并不会递归地拷贝对象的内部结构。相反,新的对象会引用原对象中的内部对象或数组。因此,如果原始对象中的某个内嵌对象被修改,浅拷贝后的对象中的相应内嵌对象也会受到影响。
关于浅拷贝,可以使用 Object.assign(target, ...sources)
或者扩展运算符(Spread Operator):
// Object.assign()let obj = { a: 1, b: { c: 2 } };let copy = Object.assign({}, obj); // Spread Operatorlet obj = { a: 1, b: { c: 2 } };let copy = { ...obj }; // 对于数组let arr = [1, 2, 3];let copy = arr.slice();
- 深拷贝(Deep Copy):深拷贝会递归地复制对象中的每一层数据,包括嵌套的对象和数组。也就是说,深拷贝会生成一个与原始对象完全独立的新对象,即使是原始对象中的内嵌对象,也会被完全复制并且与原始对象没有任何引用关系。
对于深拷贝,可以使用第三方库 Lodash 的 cloneDeep()
,或者对于没有函数或 undefined
的简单对象,可以使用JSON.parse(JSON.stringify())
。
// let obj = { a: 1, b: { c: 2 } };let deepCopy = JSON.parse(JSON.stringify(obj)); // Lodash 的 cloneDeep 方法let obj = { a: 1, b: { c: 2 } };let deepCopy = _.cloneDeep(obj);
- 手动实现深拷贝:根据具体的使用场景,考虑边界情况和特殊类型,添加或者删除相应的功能。如果遇到特殊情况,建议使用成熟的第三方库如 Lodash 的
cloneDeep
。
function deepClone(obj, hash = new WeakMap()) { // 处理基本类型和 null if (obj === null || typeof obj !== 'object') return obj; // 处理日期对象 if (obj instanceof Date) return new Date(obj); // 处理正则对象 if (obj instanceof RegExp) return new RegExp(obj); // 处理循环引用 if (hash.has(obj)) return hash.get(obj); // 获取对象的构造函数 const constructor = obj.constructor; // 创建新对象并存入 WeakMap const cloneObj = new constructor(); hash.set(obj, cloneObj); // 处理 Map if (obj instanceof Map) { obj.forEach((value, key) => { cloneObj.set(key, deepClone(value, hash)); }); return cloneObj; } // 处理 Set if (obj instanceof Set) { obj.forEach(value => { cloneObj.add(deepClone(value, hash)); }); return cloneObj; } // 处理对象和数组 for (let key in obj) { if (obj.hasOwnProperty(key)) { cloneObj[key] = deepClone(obj[key], hash); } } return cloneObj;}
数组的 sort 方法
数组元素随机排序
数组去重
debounce and throttle
- 防抖(Debouncing) 是一种编程技术,旨在限制事件触发的频率。它的作用是延迟某个操作的执行,直到事件停止触发一段时间后,才真正执行。这通常用于输入框、滚动、窗口大小调整等高频触发的事件,目的是提高性能,避免不必要的重复操作。
- 应用场景:
- 输入框自动搜索:在输入框中用户输入时,防止每输入一个字符都发起一次 API 请求,可以使用防抖技术延迟请求的执行,直到用户停止输入。
- 窗口大小调整:在调整窗口大小时,不需要每次调整都重新计算布局,防抖可以减少计算的频率,直到用户停止调整窗口大小后再进行计算。
- 滚动事件:在滚动事件中,防抖可以避免频繁执行大量的 DOM 操作或网络请求,例如懒加载图片。
function debounce(fn, ms) { let timeout; return function(...args) { // 使用剩余参数语法收集多个传入的参数 clearTimeout(timeout); timeout = setTimeout(() => fn.apply(this, args), ms); };} // 在事件触发的第一次立即执行,而后续的触发则是延迟执行function debounce(func, wait, immediate) { let timeout; return function(...args) { const context = this; const later = () => { timeout = null; if (!immediate) { func.apply(context, args); } }; const callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) { func.apply(context, args); } };}
- 节流(Throttle) 是与 防抖(Debounce)类似的一种控制函数执行频率的技术,它的目的是限制函数在单位时间内的执行次数。与防抖不同的是,节流是限制函数的执行频率,而防抖是限制函数的执行时机。节流函数会强制一个函数在特定时间间隔内只能执行一次。也就是说,不管事件触发了多少次,节流函数只会在指定的时间间隔内执行一次。节流通常应用于一些频繁触发的事件,比如 滚动、窗口大小改变、鼠标移动 等。
// 时间戳版本的节流//如果距离上次执行函数的时间大于等于 wait,就执行目标函数。function throttle(func, wait) { let lastTime = 0; return function(...args) { const now = Date.now(); if (now - lastTime >= wait) { lastTime = now; func.apply(this, args); } };} // 定时器版本的节流的简单实现// 第一次和最后一次调用可能丢失function throttle(func, wait) { let timeout; return function(...args) { if (!timeout) { timeout = setTimeout(() => { timeout = null; func.apply(this, args); }, wait); } };} // function throttle(fn, ms) { let isThrottled = false; // 标记是否处于节流状态 let savedArgs, savedThis; // 用于保存最后一次调用的参数和上下文 function wrapper(...args) { if (isThrottled) { // 如果节流中,保存调用参数和上下文 savedArgs = args; savedThis = this; return; } // 第一次调用,使用立即执行函数 fn.apply(this, args); isThrottled = true; // 在 `ms` 时间后解除节流状态 setTimeout(() => { isThrottled = false; // 执行最后一次调用(排除只有一次) if (savedArgs) { wrapper.apply(savedThis, savedArgs); savedArgs = savedThis = null; } }, ms); } return wrapper;}