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的修复时,没有其他人再看看,其中元素引用为空,但处理程序的代码仍然是执行?
删除所有旧答案,并解决编辑:)
让我们举一个更简单的例子:textContent.
var ele = document.getElementById('foo');
ele.textContent = 'abc';
ele = null;
Run Code Online (Sandbox Code Playgroud)
这个代码设置textContent的#foo到abc,并丢弃参考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)
如您所见,函数与我们分配它们的引用无关:它们与引用所指向的对象相关联.您可以从对象的任何引用中调用它们,而不仅仅是您用于分配函数的引用.
因此,您可以使引用无效以打破循环依赖关系,而不会影响对象的正确行为.
由于您ele首先是通过调用获得的document.getElementById("someParagraphId"),因此在您分配之前,它引用的对象已经存在于内存中ele。它是文档的一部分——当然,除了 之外,内存中还有对它的引用ele。