Javascript内存泄漏:为什么将对象分配给null工作?

SZR*_*SZR 6 javascript javascript-events circular-reference

关于用于防止内存泄漏的赋值到空的修复的性质,有人可以为我抓痒吗?

我们都熟悉以下技术来停止DOM对象和JS对象之间的循环引用,以防止内存泄漏:

    function foo() {
      var ele = document.getElementById("someParagraphId");

      ele.onclick = function() {
        //some action here
      };

      ele = null;
    }
Run Code Online (Sandbox Code Playgroud)

问题是为什么上述工作?将"ele"设置为null肯定会停止循环引用,但它是否也会阻止将来引用"ele"?

    function foo() {
      var ele = document.getElementById("someParagraphId");

          ele.onclick = function() {
              console.log("accessing ele ... after set to null " + ele.onclick);
          };

      ele = null;

    }
Run Code Online (Sandbox Code Playgroud)

然而事件听众却开火了.它会抱怨"ele"对象为null(这是我们所期望的).

鉴于上述行为,我们是否正确推断Javascript引擎实现将保留对事件侦听器的某种内部引用,并且在触发事件时调用此引用?

eventHandlerRef = //assignment to "ele.onclick" handler/listener;
Run Code Online (Sandbox Code Playgroud)

如果有如上所述的引用,那么赋值为null的修复是否依赖于实现?或者,它是ECMAScript规范的一部分.

从我的理解,这个修复一直是跨浏览器安全的.在应用null赋值之前,我没有遇到过许多关于检测/嗅探浏览器类型的具体提及的示例.

===============编辑==================

我认为由于我提出的方式,问题可能会在不知不觉中直接从我试图传达的内容中进行讨论.正在引用的几个概念:

对象句柄/对象引用ala:

 var obj1, obj2;
     obj1 = {foo: "bar"}; //obj1 points to the an area of the heap allocated 
                          //for the object literal.

 obj2 = obj1;     //obj2 points to the same position in the heap
                      //as obj1.

 //obj1 = null or
 obj1 = {/*another obj literal*/}  //the new object literal is created 
                                       //and allocated in some other part 
                                       //in the heap, and obj1 now points 
                                       //to the new location.

//obj2 is unaffected by obj1's re-assignment. 
Run Code Online (Sandbox Code Playgroud)

以上不是我的痒所在,我后悔添加这条线:

console.log("accessing ele ... after set to null " + ele.onclick);
Run Code Online (Sandbox Code Playgroud)

以上使得这看起来是一个封闭问题.我完全期望在原帖中指出要抛出的错误.

我的痒更多是在...的背景下,出于某种原因,在我看来,我一直认为Javascript引擎将ele.onlick()在事件触发时直接调用并将元素设置为null类似于以下流程:

var obj = {};
obj.method = function() {};

obj = null;
//error calling obj.method(), i.e., obj is null
Run Code Online (Sandbox Code Playgroud)

鉴于我们在原始帖子中知道事件处理程序仍然在ele被设置为null之后触发,在我看来,流程更类似于:

var obj = {};
obj.method = function() {};

var objRef = obj; 

obj = null;  

//The Javascript Engine calling objRef.method when event triggered.
Run Code Online (Sandbox Code Playgroud)

我的痒归结为这个问题,上面是大多数Javascript实现的工作原理,其中一些内部引用指向分配的事件处理程序,并且在触发事件时调用此内部引用?

或者不同的措辞,是什么阻止从调用JavaScript引擎实现 ele.onclick()直接(撇开暂时的设计和架构问题)?

也许我的思维过程的工作方式不同,但是当他们第一次遇到赋值为null的修复时,没有其他人再看看,其中元素引用为空,但处理程序的代码仍然是执行?

zne*_*eak 6

删除所有旧答案,并解决编辑:)

让我们举一个更简单的例子:textContent.

var ele = document.getElementById('foo');
ele.textContent = 'abc';
ele = null;
Run Code Online (Sandbox Code Playgroud)

这个代码设置textContent#fooabc,并丢弃参考ele.如果我#foo再次要求会怎么样?...

var ele = document.getElementById('foo');
ele.textContent = 'abc';
ele = null;
ele = document.getElementById('foo');
console.log('#foo is ' + ele.textContent);
Run Code Online (Sandbox Code Playgroud)

它会记录"#foo is abc".这只是表明#foo在我的脚本之外生活,并document保留对它的引用(因为我让它回来调用方法document).它与事件处理程序的工作方式相同.

var ele = document.getElementById('foo');
ele.onclick = function() { console.log('abc'); };
ele = null;
ele = document.getElementById('foo');
console.log('#foo.onclick is ' + ele.onclick);
Run Code Online (Sandbox Code Playgroud)

事件处理程序不是一种特殊的属性.它们只是您编写引用(函数)的变量.这些是大致功能性的语言功能,其中功能可以用作简单的值.JavaScript实现只是从DOM引用调用事件处理程序,而不是从ele引用调用.让我们再做一个更简单的例子document.getElementById.

var a = {};
var b = a;
b.foo = function() { console.log("hello world!"); };
console.log(a.foo + " is the same as " + b.foo);
b = null;
console.log("Even though b is " + b + ", a.foo is still " + a.foo);
a.foo();
Run Code Online (Sandbox Code Playgroud)

如您所见,函数与我们分配它们的引用无关:它们与引用所指向的对象相关联.您可以从对象的任何引用中调用它们,而不仅仅是您用于分配函数的引用.

因此,您可以使引用无效以打破循环依赖关系,而不会影响对象的正确行为.


tru*_*ity 0

由于您ele首先是通过调用获得的document.getElementById("someParagraphId"),因此在您分配之前,它引用的对象已经存在于内存中ele。它是文档的一部分——当然,除了 之外,内存中还有对它的引用ele