javascript - Javascript 中依赖于其范围之外的变量的函数会导致内存泄漏吗?

标签 javascript performance memory

考虑以下代码,该代码创建附加到 web map 中的点的按钮列表。

最初我考虑扩展元素并将点添加为属性,但因为那不是 recommended ,我只是在事件函数中引用该对象。

 pointsInMap = [
    {
      x : 20.2,
      y : 15.2,
      name : "point1"
    },
    {
      x : 20.2,
      y : 15.2,
      name : "point2"
    },
    {
      x : 20.2,
      y : 15.2,
      name : "point3"
    }
  ]

  function addZoomToButtons(points){
    points.forEach( point => {
      const button = document.createElement('button');
      button.text = `Zoom to ${point.name}`;
      //I'm overwriting this variable 3 times but javascript needs to keep reference to it, where will it be stored since it's outside the event scope?
      const pointObj = new Point(point.x, point.y)
      button.addEventListener( () => {
        myMap.panAndZoomToPoint(pointObj)
      })
      document.body.appendChild(button);
    })
  }

  addZoomtoButtons(pointsInMap);

上面的代码在性能/内存方面有什么“错误”吗?我觉得有,但我对 Javascript 中的内存管理了解不够,无法弄清楚。

如果没有任何问题,请帮助我理解原因。

在我看来,不仅仅是添加到内存中 3 events ,它还将保留 addZoomToButtons 的 3 个“副本”/forEach函数作用域,因为它具有 events 的变量要求。 这只是一个小例子,但请考虑事情可能会变得非常大。

代码调用const pointObj = new Point(point.x, point.y)在事件范围之外 3 次,但它不能仅仅覆盖 pointObj因为事件引用了它,所以它也不能只是将范围转移到事件内部,所以我假设紧邻事件外部的范围也被不必要地存储。

如果出现问题,设计解决方案的另一种方法是什么?

表达这个问题的另一种方式是如何以正确且推荐的方式将对象引用绑定(bind)到事件?

最佳答案

I'm overwriting this variable 3 times but javascript needs to keep reference to it, where will it be stored since it's outside the event scope?

JS 将其保存在闭包中。什么是闭包?您可能知道prototype对象,闭包类似,但使用标识符而不是属性。

const obj = {};

obj.__proto__.someProp = "someProp";

console.log(obj.someProp); // logs "someProp"

obj没有自己的someProp property,JS 沿着原型(prototype)链向上查找其原型(prototype)对象上的 prop。

标识符的情况非常相似。您可以将函数视为具有隐藏的 __closure__存储来自上层范围的标识符的对象。如果在本地范围内找不到标识符,则检查最近的闭包。如果在那里没有找到,则检查闭包的闭包。

var global = "global";

function f() {
  var outer = "outer";
  function g() {
    var local = "local";
  }
};

/*

in g:
g.__localScope__ has one identifier: `local`
g.__closure__ has one identifier: `outer`
g.__closure__.__closure__ is __global_scope__ and has identifier `global`

*/

当你有从其他函数中提取到上层作用域的函数时

function f(k) {
  return function g() {
    console.log(k);
  }
}


const g1 = f(1);
const g2 = f(2);

/*

  g1.__closure__ has identifier k that has value: 1
  g2.__closure__ also has identifier k but it has value: 2
  
  g1 can't acces k of g2 and vice versa
  
*/

g1()  // 1
g2()  // 2

您可以在范围面板下的开发工具中看到闭包链: enter image description here

很高兴知道现代 JavaScript 引擎非常高效并且有很多非常聪明的优化。

考虑这段代码:

function f(k) {
  const l = "l value";
  return function g() {
    debugger;
    console.log(k);
  };
}

f(1)();

事件虽然l位于 g() 的外部范围内函数未在开发工具的范围面板中列出:

enter image description here

因为g()内部没有使用这个标识符函数,Chrome JS引擎V8不会将其保留在内存中以节省资源。如果将日志语句更改为 console.log(k, l); ,这两个变量都将在开发工具中可见并可访问:

enter image description here

关于javascript - Javascript 中依赖于其范围之外的变量的函数会导致内存泄漏吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53618507/

相关文章:

javascript - 如何在 JavaScript 对象文字中使用变量作为键?

javascript - contentEditable 游标的父元素

Mysql IN 子句与自动递增 ID 的范围

sql - 使用 SQL 逻辑 'OR' 和运算符 'LIKE' 时,如何调整 PostgreSQL 查询优化器?

c++ - 为什么我的程序在我的 CPU 设备上运行速度明显快于我的 GPU 设备?

php - Codeigniter AJAX 示例

javascript - 如何只获取原型(prototype)链的属性而不是对象本身的属性?

.net - 服务主机效率

C++:请求超过2GB的内存

memory - CUDA - 合并内存访问和总线宽度