代码之家  ›  专栏  ›  技术社区  ›  Mojimi

依赖于其作用域之外的变量的函数会导致JavaScript内存泄漏?

  •  0
  • Mojimi  · 技术社区  · 5 年前

    考虑下面的代码,它创建了一个按钮列表,这些按钮附加到Web地图中的一个点上。

    最初我考虑扩展元素并将点作为属性添加,但由于这不是 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 / forEach 函数作用域,因为它具有 事件 要求。 这只是一个小例子,但请考虑到事情可能会变得非常大。

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

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

    另一种表达这个问题的方法是,如何以适当和推荐的方式将对象引用绑定到事件?

    1 回复  |  直到 5 年前
        1
  •  1
  •   marzelin    5 年前

    我三次重写这个变量,但javascript需要保留 引用它,因为它在事件之外,它将存储在哪里 范围?

    JS将其保持在一个封闭状态。什么是关闭?你可能知道 prototype 对象,闭包是类似的,但使用标识符而不是属性。

    const obj = {};
    
    obj.__proto__.someProp = "someProp";
    
    console.log(obj.someProp); // logs "someProp"
    

    自从 obj 没有自己的 someProp 属性,JS进入原型链并在其原型对象上找到该属性。

    标识符的情况非常相似。你可以把函数看作是隐藏的 __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() 函数它不在dev tools的scope面板中列出:

    enter image description here

    因为此标识符未在 g-() 函数,ChromeJSEngineV8不会将其保存在内存中以节省资源。如果将LOG语句更改为 console.log(k, l); ,这两个变量将在dev工具中可见和可访问:

    enter image description here