JavaScript 高级技巧:深拷贝、节流与防抖详解

一、深拷贝 (Deep Copy)

什么是深拷贝?

深拷贝是指创建一个新对象,完全复制原始对象的所有属性,包括嵌套对象和数组,使得新对象与原始对象完全独立,修改新对象不会影响原始对象。

实现深拷贝的方法

1. JSON 方法(简单但有局限)
function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

局限

  • 无法处理函数、Symbol、undefined
  • 无法处理循环引用
  • 会忽略对象的原型链
  • Date 对象会变成字符串
2. 递归实现(完整版)
function deepClone(obj, hash = new WeakMap()) {
  // 处理基本数据类型和null
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  // 处理循环引用
  if (hash.has(obj)) {
    return hash.get(obj);
  }
  
  // 处理Date和RegExp
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  
  // 创建新对象或数组
  const cloneObj = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
  
  // 保存到hash中,用于处理循环引用
  hash.set(obj, cloneObj);
  
  // 递归拷贝所有属性
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }
  
  // 处理Symbol属性
  const symbolKeys = Object.getOwnPropertySymbols(obj);
  for (let key of symbolKeys) {
    cloneObj[key] = deepClone(obj[key], hash);
  }
  
  return cloneObj;
}
3. 使用第三方库
  • Lodash 的 _.cloneDeep()
  • jQuery 的 $.extend(true, {}, obj)

二、节流 (Throttle)

什么是节流?

节流是指在一个连续的操作中,无论操作多么频繁,都按照固定的时间间隔执行函数。比如:滚动事件、鼠标移动事件等高频触发的事件。

实现节流的方法

1. 时间戳版
function throttle(fn, delay) {
  let prev = 0;
  return function(...args) {
    const now = Date.now();
    if (now - prev > delay) {
      fn.apply(this, args);
      prev = now;
    }
  };
}
2. 定时器版
function throttle(fn, delay) {
  let timer = null;
  return function(...args) {
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(this, args);
        timer = null;
      }, delay);
    }
  };
}
3. 结合版(最常用)
function throttle(fn, delay) {
  let timer = null;
  let prev = 0;
  return function(...args) {
    const now = Date.now();
    const remaining = delay - (now - prev);
    
    if (remaining <= 0 || remaining > delay) {
      if (timer) {
        clearTimeout(timer);
        timer = null;
      }
      prev = now;
      fn.apply(this, args);
    } else if (!timer) {
      timer = setTimeout(() => {
        prev = Date.now();
        timer = null;
        fn.apply(this, args);
      }, remaining);
    }
  };
}

使用场景

  • 窗口调整(resize)
  • 滚动事件(scroll)
  • 鼠标移动(mousemove)
  • 输入框实时搜索(可以结合防抖)

三、防抖 (Debounce)

什么是防抖?

防抖是指在事件被触发后,等待一段时间再执行函数,如果在这段时间内事件又被触发,则重新计时。适用于连续事件结束后触发一次的场景。

实现防抖的方法

1. 基础版
function debounce(fn, delay) {
  let timer = null;
  return function(...args) {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
      timer = null;
    }, delay);
  };
}
2. 立即执行版
function debounce(fn, delay, immediate = false) {
  let timer = null;
  return function(...args) {
    if (timer) clearTimeout(timer);
    
    if (immediate) {
      const callNow = !timer;
      timer = setTimeout(() => {
        timer = null;
      }, delay);
      if (callNow) fn.apply(this, args);
    } else {
      timer = setTimeout(() => {
        fn.apply(this, args);
        timer = null;
      }, delay);
    }
  };
}
3. 返回值版
function debounce(fn, delay, immediate = false) {
  let timer = null;
  let result;
  
  return function(...args) {
    if (timer) clearTimeout(timer);
    
    if (immediate) {
      const callNow = !timer;
      timer = setTimeout(() => {
        timer = null;
      }, delay);
      if (callNow) result = fn.apply(this, args);
    } else {
      timer = setTimeout(() => {
        fn.apply(this, args);
        timer = null;
      }, delay);
    }
    
    return result;
  };
}

使用场景

  • 搜索框输入(等待用户停止输入后再发送请求)
  • 窗口大小调整完成后的计算
  • 表单验证(等待用户停止输入后验证)
  • 按钮连续点击(防止重复提交)

四、节流与防抖的区别

特性节流 (Throttle)防抖 (Debounce)
执行时机固定时间间隔执行一次停止触发后一段时间执行
适用场景高频连续事件,需要均匀响应连续事件结束后触发一次
比喻水龙头滴水(固定频率)电梯关门(最后一个人进入后关门)

五、实际应用示例

1. 节流示例 - 滚动加载更多

window.addEventListener('scroll', throttle(function() {
  if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
    loadMoreData();
  }
}, 200));

2. 防抖示例 - 搜索框

const input = document.querySelector('input');
input.addEventListener('input', debounce(function(e) {
  fetchResults(e.target.value);
}, 500));

3. 组合使用示例

// 输入时防抖,提交时立即执行
const searchInput = document.querySelector('#search');
const searchButton = document.querySelector('#search-btn');

const debouncedSearch = debounce(performSearch, 500);

searchInput.addEventListener('input', (e) => {
  debouncedSearch(e.target.value);
});

searchButton.addEventListener('click', () => {
  debouncedSearch.cancel(); // 取消防抖计时
  performSearch(searchInput.value); // 立即执行
});

function performSearch(query) {
  // 执行搜索逻辑
}

// 在防抖函数中添加cancel方法
function debounce(fn, delay) {
  let timer = null;
  
  function debounced(...args) {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
      timer = null;
    }, delay);
  }
  
  debounced.cancel = () => {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
  };
  
  return debounced;
}

六、总结

  1. 深拷贝:用于完全复制复杂对象,需要注意处理各种特殊情况(循环引用、Date、RegExp等)
  2. 节流:控制函数执行频率,适用于需要均匀响应的高频事件
  3. 防抖:延迟函数执行,适用于连续事件结束后的操作

在实际开发中,可以根据具体场景选择合适的实现方式,或者使用成熟的工具库如Lodash中的_.cloneDeep()_.throttle()_.debounce()方法。

附:前端学习资料大全

百度网盘:链接: https://pan.baidu.com/s/1OTc6cFnjq4Y1vp_itHL_qA?pwd=hxj4 提取码: hxj4 

诸君共勉!有问题,可以评论区多多发言~

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值