基础-内存泄漏与闭包

内存泄漏和闭包可以说是最基本的内容了,但要清楚他们之间的的关系,则得先了解什么是垃圾回收。

1. 垃圾回收

那什么是垃圾 呢?一直以来也没有明确的定义,但都形成了一个共识,那就是不再需要的内存,这些内存里的数据就是垃圾。那什么又是不再需要呢?这个是由开发者决定的,需不需要取决于你。 比如说:

js 复制代码
      function createIncrease() {
        const doms = new Array(100).fill(0).map((_, index) => {
          const dom = document.createElement("div");
          dom.innerHTML = index;
          return dom;
        });

        return function increase() {
          doms.forEach((dom) => {
            dom.innerHTML = Number(dom.innerHTML) + 1;
          });
        };
      }
      const increase = createIncrease();
      document.querySelector("button").addEventListener("click", increase);

这个createIncrease创建了100div,原来的div的内容是下标,从0开始。里面有个子函数increase,循环所有的div把内容+1

这个很标准的闭包了,拿到子函数,也拿到了上面声明的doms,从运行过程来看,每次点击按钮,使用这个子函数时,是需要这个doms的。所以这些doms是不是垃圾?很明显不是,因为我要使用它。垃圾与占用内容空间的大小无关。

再比如说:

js 复制代码
      const arr = [1, 2, 3, 4, 5];
      const sum = arr.reduce((total, num) => total + num, 0);
      console.log('sum',sum);

对数组进行求和,第一次运行结束之后,那[1, 2, 3, 4, 5]是需要的还是不需要呢?是结束了,还是后面还要用到?所以回到上面,这取决于你。

所以说,现在有一些你认为的不再需要的内容,那它们就是垃圾。js语言里面有个垃圾回收器,它可以帮助我们回收那些不再需要的内容。😂问题又来了,它怎么知道我们不再需要了。嘿,连我们自己都不知道,它知道就离谱了。虽然它不知道,但是啊,它还是很大胆的,它做出假设,虽然不知道你需要哪些东西,但是你访问不了的东西,你肯定是不想要的。比如上面的例子,

js 复制代码
   let arr = [1, 2, 3, 4, 5];
      arr = [4, 5, 6, 7, 8, 9];
      const sum = arr.reduce((total, num) => total + num, 0);
      console.log("sum", sum);

arr重新赋值,那初始化的那内容空间还有机会访问吗?没了,不可能访问到了,所以回收机制就认为这个就是你不需要的。

所以垃圾回收器回收的是那些我们认为不想要的,且无法触达的内存空间。那不想要的有很多,无法触达的知识其中的一小部分,还有一部分是可以触达的,所以垃圾回收器无法回收,它们就一直存在那里,这就是内存泄漏

那当这个内存泄漏变大了之后,就会严重影响我们程序的运行,那如何解决内存泄漏?就是让这些内容变得不可触达就可以了。比如说,

js 复制代码
  let arr = [1, 2, 3, 4, 5];
      const sum = arr.reduce((total, num) => total + num, 0);
      console.log("sum", sum);
      arr = null;

在程序运行结束后,令arr = null,那[1, 2, 3, 4, 5];内存空间就变得不可触达,就能被垃圾回收器处理了。

又比如第一个,点击第一次之后就不需要了。

js 复制代码
 const btn = document.querySelector("button");
      let increase = createIncrease();

      function handle() {
        increase();
        btn.removeEventListener("click");
        increase = null;
      }
      btn.addEventListener("click", handle);

第一次点击过后,就移除监听器,然后令increase = null,那之前的函数就变得不可触达,然后被回收。

哎,说到这里,怎么都是垃圾回收和内存泄漏,那内存泄漏和闭包的关系呢?从上面的例子看,好像没有什么直接的关系,只是闭包会让我们放松警惕。像例2,只需要在最后令arr = null,就可以了。但是闭包得绕一下,你只是认为以后不用这个函数了,函数能有多大的内存空间,所以就不理了。😳但是闭包所关联的那个环境,把doms保留下来了。所以不是流传着那句话闭包容易引起内存泄漏,慎用闭包嘛。

闭包的内存泄漏和其他内存泄漏没有啥本质的区别:

1.持有了不再需要的函数引用,会导致函数关联的词法环境无法销毁,从而导致内存泄漏。

2.当多个函数共享词法环境时,会导致词法环境膨胀,从而导致无法触达也无法回收的内容空间。

js 复制代码
   function createIncrease() {
        const doms = new Array(100).fill(0).map((_, index) => {
          const dom = document.createElement("div");
          dom.innerHTML = index;
          return dom;
        });

        function increase() {}
        function tem() {
          doms;
        }

        return increase;
      }

      const btn = document.querySelector("button");
      let increase;
      btn.addEventListener("click", () => {
        increase = createIncrease();
      });

点击按钮,但是这个函数没有用到闭包环境里的domsdoms被另一个子函数使用了。点击按钮后,在控制台手动垃圾回收,再比较前后的内存,发现内存没被回收。

这就是因为tem函数的存在,它们的闭包环境都是一样的,共享一个词法环境。如果说只有一个子函数,那如果没有用到,浏览器就会把doms优化掉,这就不会造成内存泄漏。但是现在有另一个函数使用了doms,那浏览器不敢回收。后面返回increase,没用到doms,但是词法环境仍然保留了这个doms

相关推荐
不爱写程序的东方不败2 小时前
APP接口测试流程实战Posman+Fiddler
前端·测试工具·fiddler
晚霞的不甘3 小时前
Flutter for OpenHarmony构建全功能视差侧滑菜单系统:从动效设计到多页面导航的完整实践
前端·学习·flutter·microsoft·前端框架·交互
黎子越3 小时前
python相关练习
java·前端·python
北极糊的狐3 小时前
若依项目vue前端启动键入npm run dev 报错:不是内部或外部命令,也不是可运行的程序或批处理文件。
前端·javascript·vue.js
XRJ040618xrj3 小时前
Nginx下构建PC站点
服务器·前端·nginx
We་ct3 小时前
LeetCode 289. 生命游戏:题解+优化,从基础到原地最优
前端·算法·leetcode·矩阵·typescript
有诺千金4 小时前
VUE3入门很简单(4)---组件通信(props)
前端·javascript·vue.js
2501_944711434 小时前
Vue-路由懒加载与组件懒加载
前端·javascript·vue.js
雨季6664 小时前
Flutter 三端应用实战:OpenHarmony “心流之泉”——在碎片洪流中,为你筑一眼专注的清泉
开发语言·前端·flutter·交互
换日线°5 小时前
前端3D炫酷展开效果
前端·3d