闭包相关内容

闭包

描述:闭包就是能够读取其他函数内部变量的函数。

闭泡的特性:

  • 函数内再嵌套函数
  • 内部函数可以引用外层的参数和变量
  • 参数和变量不会被垃圾回收机制回收

创建闭包

提示:创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域。

例如:

function createCounter() {
  let count = 0;
  return function() {
    count++;
    return count;
  };
}
 
const counter = createCounter();
console.log(counter()); // 输出: 1
console.log(counter()); // 输出: 2
console.log(counter()); // 输出: 3

闭包的使用

提示:闭包可以用来实现缓存(也称为记忆化),用于存储和重用计算结果,以避免重复计算。缓存通常用于那些执行成本高昂的函数,如复杂的计算、网络请求或数据库查询。

function createCacheCalculator(func) {
  // 创建一个缓存对象
  const cache = {};
  // 返回一个闭包,它具有访问外部函数变量的权限
  return function(...args) {
    // 创建一个缓存键,通常可以使用参数的字符串表示来生成
    const cacheKey = JSON.stringify(args);
    // 检查缓存中是否存在该键的结果
    if (cacheKey in cache) {
      console.log('使用缓存值:', args);
      return cache[cacheKey];
    } else {
      // 计算结果并存储到缓存中
      const result = func.apply(this, args);
      cache[cacheKey] = result;
      console.log('新的值:', args);
      return result;
    }
  };
}

// 示例:使用缓存的计算器
const cachedFactorial = createCacheCalculator((n) => {
  if (n === 0 || n === 1) return 1;
  return n * cachedFactorial(n - 1);
});

console.log(cachedFactorial(5)); // 计算新值
console.log(cachedFactorial(5)); // 使用缓存值
console.log(cachedFactorial(10)); // 计算新值

内存泄露

提示:JavaScript内存泄漏是指由于不正确的内存管理而导致的内存占用不断增加,最终导致应用程序性能下降甚至崩溃的问题

例如:

  1. 未正确清理事件监听器: 如果在DOM元素上添加了事件监听器,但是忘记在不需要时将其删除,就会导致内存泄漏。确保在不需要监听事件时,使用removeEventListener方法将监听器从元素上移除。

    // 添加事件监听器
    element.addEventListener('click', handleClick);
    
    // 移除事件监听器
    element.removeEventListener('click', handleClick);
    
  2. 闭包引用: 当一个函数内部定义了另一个函数,并且内部函数引用了外部函数的变量时,如果这个内部函数被外部作用域之外的东西引用,就会导致闭包。如果闭包持续存在,外部函数的变量将无法被垃圾回收,从而导致内存泄漏。确保在不需要的时候,解除对闭包的引用。

    function outerFunction() {
      var someVariable = 'Hello';
      // 内部函数引用外部变量
      function innerFunction() {
        console.log(someVariable);
      }
      return innerFunction;
    }
    var closure = outerFunction();
    // 当不再需要闭包时,解除引用
    closure = null;
    
  3. 定时器未清除: 如果设置了循环定时器或者延时定时器,并且在不需要时未清除它们,将会导致内存泄漏。确保在不再需要定时器时,使用clearInterval或clearTimeout将其清除。

    // 设置定时器
    var timer = setInterval(function() {
      // 执行操作
    }, 1000);
    
    // 当不再需要定时器时,清除它
    clearInterval(timer);
    
  4. 大对象引用: 如果将一个大对象赋值给一个全局变量或者缓存中,并且在不需要时未清除这个引用,将会导致内存泄漏。确保在不再需要这些大对象时,将其引用置为null,以便垃圾回收器能够释放内存。

    // 将大对象赋值给全局变量
    var largeObject = { /* 大对象 */ };
    
    // 当不再需要大对象时,将引用置为null
    largeObject = null;
    
  5. DOM节点未正确清理: 当移除DOM节点时,如果节点上仍然存在事件监听器、引用或者数据,就会导致内存泄漏。确保在移除DOM节点时,清理所有相关的引用。

    // 移除DOM节点
    element.parentNode.removeChild(element);
    

解决内存泄露方法

提示:内存泄漏发生在不再被引用的数据没有被垃圾回收器回收时

例如:

  1. 缓存大小限制: 限制缓存的大小,当达到最大容量时,可以移除最老的条目。这可以通过使用一个队列(先进先出)或者最近最少使用(LRU)算法来实现。
  2. 缓存键的有效管理: 确保缓存键的唯一性和准确性,避免因为键的不准确而导致无法从缓存中移除不需要的条目。
  3. 缓存条目的生命周期: 为缓存条目设置一个过期时间,使用定时器或者在特定条件下清除过期的缓存数据。
  4. 监听相关事件: 如果缓存的数据依赖于外部资源(如数据库条目、API数据等),可以监听这些资源的变化事件,并在数据变更时清除或更新缓存。
  5. 垃圾回收的触发: 在确定不再需要缓存时,手动删除缓存对象或设置为null,以允许垃圾回收器回收内存。
  6. WeakMaps的使用: 使用WeakMap来存储缓存数据,WeakMap的键是弱引用,当键不再被其他对象引用时,WeakMap中的条目会被垃圾回收器自动移除。
  7. 使用第三方库: 使用已经实现缓存策略的第三方库,如lodash的memoize函数,这些库通常提供了良好的内存管理机制。
  8. 定期清理缓存: 定期检查缓存并清理不再需要的条目,特别是在应用程序的生命周期事件(如页面导航或组件卸载)中进行清理。
  9. 避免缓存大型对象: 如果可能,避免缓存大型对象或数据集,或者只缓存数据的关键部分。
  10. 缓存数据的序列化: 考虑缓存数据的大小,避免缓存大量未压缩的数据,可以使用序列化方法减少存储的数据量。
  11. 监控内存使用: 使用开发工具监控内存使用情况,及时发现内存泄漏的迹象。
  12. 使用请求响应模型: 对于Web应用,可以在请求结束时清理与请求相关的缓存。

解决闭包的方法

  1. 手动清除缓存: 提供一个清除缓存的方法,允许开发者在适当的时候手动清除缓存。

    	function createCacheCalculator(func) {
    	  const cache = {};
    	  const calculator = function(...args) {
    	    // ...(缓存逻辑)
    	  };
    	  calculator.clearCache = function() {
    	    cache = {};
    	  };
    	  return calculator;
    	}
    
  2. 设置缓存的生命周期: 如果缓存与某个特定的对象或组件相关联,可以在对象或组件被销毁时清除缓存。

    class SomeComponent {
      constructor() {
        this.cachedCalculator = createCacheCalculator(func);
      }
      componentWillUnmount() {
        this.cachedCalculator.clearCache();
      }
    }
    
  3. 使用 WeakMap: 使用 WeakMap 来存储缓存数据,这样当没有其他引用指向缓存的键时,WeakMap 中的对象可以被垃圾回收器回收。

    function createCacheCalculator(func) {
      const cache = new WeakMap();
      // ...(缓存逻辑,使用 WeakMap API)
    }
    
  4. 缓存自动过期: 为缓存项设置一个过期时间,当缓存项达到一定的时间后,自动从缓存中移除。
    一个简单的LRU缓存实现示例

    function createCacheCalculator(func) {
      const cache = {};
      return function(...args) {
        const key = JSON.stringify(args);
        if (key in cache) {
          const cached = cache[key];
          if (Date.now() - cached.timestamp > CACHE_EXPIRATION_TIME) {
            delete cache[key];
          } else {
            return cached.value;
          }
        }
        // ...(计算并缓存新值)
      };
    }
    const CACHE_EXPIRATION_TIME = 60 * 60 * 1000; // 1 hour
    
  5. 使用第三方库: 使用第三方库,如 lodash 的 memoize 函数,这些库通常提供了缓存清除的机制。

  6. 依赖注入: 通过依赖注入的方式,将缓存对象作为参数传递给 createCacheCalculator 函数,这样当需要清除缓存时,只需重新注入一个新的缓存对象即可。

一个简单的LRU缓存实现示例:

class LRUCache {
  constructor(limit) {
    this.cache = {};
    this.size = 0;
    this.limit = limit;
  }
  get(key) {
    if (key in this.cache) {
      this.touch(key);
      return this.cache[key];
    }
  }
  set(key, value) {
    if (this.size >= this.limit) {
      this.evict();
    }
    this.cache[key] = value;
    this.size++;
    this.touch(key);
  }
  touch(key) {
    if (!(key in this.cache)) {
      return;
    }
    const value = this.cache[key];
    delete this.cache[key];
    this.cache[key] = value;
  }
  evict() {
    const key = Object.keys(this.cache)[0];
    delete this.cache[key];
    this.size--;
  }
}
  • 19
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值