这个问题是关于jQuery函数如何保存HTML元素,以及是否会对垃圾收集产生影响。
考虑以下两个函数,它们显示或隐藏特定<div>元素。
function showError() { $("div#error").show(); }
function hideError() { $("div#error").hide(); }
这两个函数总是调用jQuery来查找<div>元素;它们可以被重构一次以消除jQuery函数的开销,例如:
var divError = $("div#error");
function showError() { divError.show(); }
function hideError() { divError.hide(); }
但在这种情况下,divError
变量在全局范围内是宽松的;我们可以用一个函数来实现一些技巧:
var showError, hideError;
(function() {
var divError = $("div#error");
showError = function() { divError.show(); }
hideError = function() { divError.hide(); }
})();
在所有这些情况下,对垃圾收集的影响是什么?调用jQuery函数的结果在全局范围内浮动是否有问题?
尽量减少对jQuery函数的调用是一个好的规程吗?应该效仿还是回避这些例子?
如果我将这些示例中的jQuery替换为document.getElementById()
,以检索HTML元素,是否也存在同样的问题?
垃圾回收
如果你正在做其中的一些,那么不要担心。这里显示的任何一个用户案例如果单独使用都不会对内存使用产生负面影响。
内存占用
当在大循环中完成时,这些将开始产生潜在的影响。每次创建函数对象时,它可能占用多达754字节的内存。如果你创建了很多这样的浏览器,比如1000,你可能会短暂地达到1MB的内存,这取决于浏览器,这是可以忽略的。10000会变得更糟(~10MB),100000(~100MB)会变得麻烦,1000000(~1GB)可能会开始导致一些严重的问题,具体取决于客户端,表现为几分钟的UI锁定。
范围
当对象未保留在当前执行上下文中时,垃圾收集器将成为以前情况下的一个因素。每个执行上下文都有一个词法和变量环境。变量环境是指一旦当前执行上下文失去作用域,就有资格进行垃圾回收的环境。不过,需要注意的是,符合条件仅意味着它将在某个时候发生,并且没有承诺何时发生。
为了减少不断增长的内存占用,确保函数的执行上下文实现为尽可能快地失去作用域,将确保垃圾收集器可以选择尽可能快的进行收集。同样,收集的选择可能在一段时间内无法实现。
实施
这一切的结果是关闭变量,这样一旦它们的引用消失,它们就有资格在将来垃圾收集器方便的时候进行收集。
性能
除此之外,如果单独使用jQuery函数,那么调用jQuery函数的问题就不重要了。重复查询成为问题的唯一时间是当您拥有大约10000个以上的DOM元素时,此时您可能已经存在其他问题;当你要迭代数千次,并且希望避免毫秒堆积时。优化任何总共花费不到10毫秒的事情都可能是一种微观优化。10-100是自由裁量的,超过100是绝对必要的。
此外,任何时候通过id访问元素,都会通过O(1)字典(哈希表)查找直接访问。这意味着您的DOM将不需要完全解析。然而,情况可能并不总是如此,所以我认为我们不能就此打住。
因此,如果只使用一次,那么第一个场景没有错(这里不需要使用div,id是O(1)查找)。
function showError() { $("#error").show(); }
function hideError() { $("#error").hide(); }
然而,如果由于某种原因,id的字符串是一个类选择器,并且这些都是在循环中以某种方式大量完成的,那么您将需要确保函数定义的范围是循环,并且元素选择器是缓存的(缓存本身引入了一个有趣的附带情况,即缓存的元素集可能实际上与动态页面中的类选择器之类的页面集不匹配,但这是一个边缘情况)。
范围
父作用域很重要,因为您从来都不想在全局作用域中执行代码。因此,当这两个完成相同的事情
var divError = $("div#error");
function showError() { divError.show(); }
function hideError() { divError.hide(); }
和
var showError, hideError;
(function() {
var divError = $("div#error");
showError = function() { divError.show(); }
hideError = function() { divError.hide(); }
})();
(注意,因为IIFE中的匿名函数的变量环境被其词法环境引用,因此在该词法环境符合条件之前,它将不符合垃圾收集的条件)
他们对待范围的方式略有不同。第一个将公开变量divError,而第二个则不公开。在狭窄的环境中,这并不重要,因为词法环境和变量环境中发生冲突的可能性较小,但在全局命名空间中,这可能会有问题,因为在卸载整个页面之前,不会收集词法环境。这被称为污染全局命名空间。
替代方案
最后,整个分析归结为一些本应微不足道的事情。如果您想对该元素进行全局访问,请将其存储在一个已在使用的安全全局变量中。由于您使用的是jQuery,因此您可以简单地使用$。
$.errorDiv = $("#error");
当你想切换它时,只需使用toggle:)或者,如果你不确定状态,可以隐藏/显示,或者以某种方式调用它显示两次。
$.errorDiv.toggle();
$.errorDiv.show();
$.errorDiv.hide();
据我所知,最后一节可能并不直接适用,因为这可能是类似行为的简单再现。