jQuery 通过 internal API jQuery._data() 跟踪事件和其他类型的数据,但由于此方法是内部的,因此没有官方支持。
内部方法具有以下签名:
jQuery._data( DOMElement, data)
因此,例如,我们将检索附加到元素的所有事件处理程序(通过 jQuery):
var allEvents = jQuery._data( document, 'events');
这会返回 Object,其中包含 事件类型 作为键,事件处理程序数组 作为值。
现在如果你想获取特定类型的所有事件处理程序,我们可以这样写:
var clickHandlers = (jQuery._data(document, 'events') || {}).click;
这将返回 “click”事件处理程序的 Array 或 undefined(如果指定的事件未绑定到元素)。
为什么我要讲这个方法?因为它允许我们追踪事件委托和直接附加的事件监听器,这样我们就可以找出一个事件处理程序多次绑定到同一个元素,导致内存泄漏。
但是如果你也想在没有 jQuery 的情况下实现类似的功能,你可以通过 getEventHandlers
方法来实现
看看这篇有用的文章:
调试
我们将编写一个简单的函数来打印事件处理程序及其命名空间(如果已指定)
function writeEventHandlers (dom, event) {
jQuery._data(dom, 'events')[event].forEach(function (item) {
console.info(new Array(40).join("-"));
console.log("%cnamespace: " + item.namespace, "color:orangered");
console.log(item.handler.toString());
});
}
使用这个功能很简单:
writeEventHandlers(window, "resize");
我编写了一些实用程序,让我们可以跟踪绑定到 DOM 元素的事件
如果您关心性能,您会发现以下链接很有用:
我鼓励任何阅读这篇文章的人注意我们代码中的内存分配,我了解到由于三个重要原因而出现的性能问题:
- 内存
- 内存
- 是的,内存。
事件:良好做法
创建命名函数以便从 DOM 元素绑定和取消绑定 事件处理程序是个好主意。
如果您正在动态创建 DOM 元素,例如,为某些事件添加处理程序,您可以考虑使用 事件委托 而不是将事件侦听器直接绑定到每个元素,这样,父动态添加的元素将处理该事件。此外,如果您使用的是 jQuery,您可以命名事件;)
//the worse!
$(".my-elements").click(function(){});
//not good, anonymous function can not be unbinded
$(".my-element").on("click", function(){});
//better, named function can be unbinded
$(".my-element").on("click", onClickHandler);
$(".my-element").off("click", onClickHandler);
//delegate! it is bound just one time to a parent element
$("#wrapper").on("click.nsFeature", ".my-elements", onClickMyElement);
//ensure the event handler is not bound several times
$("#wrapper")
.off(".nsFeature1 .nsFeature2") //unbind event handlers by namespace
.on("click.nsFeature1", ".show-popup", onShowPopup)
.on("click.nsFeature2", ".show-tooltip", onShowTooltip);
循环引用
虽然 circular references 对于那些在 Garbage Collector 中实现 Mark-and-sweep algorithm 的浏览器不再是问题,但使用这种如果我们正在交换数据,则对象,因为(目前)不可能序列化为 JSON,但在未来的版本中,由于处理此类对象的新算法,这将成为可能。我们来看一个例子:
var o1 = {};
o2 = {};
o1.a = o2; // o1 references o2
o2.a = o1; // o2 references o1
//now we try to serialize to JSON
var json = JSON.stringify(o1);
//we get:"Uncaught TypeError: Converting circular structure to JSON"
现在让我们试试另一个例子
var freeman = {
name: "Gordon Freeman",
friends: ["Barney Calhoun"]
};
var david = {
name: "David Rivera",
friends: ["John Carmack"]
};
//we create a circular reference
freeman.friends.push(david); //freeman references david
david.friends.push(freeman); //david references freeman
//now we try to serialize to JSON
var json = JSON.stringify(freeman);
//we get:"Uncaught TypeError: Converting circular structure to JSON"
PD:这篇文章是关于Cloning Objects in JavaScript。此要点还包含有关使用循环引用克隆对象的演示:clone.js
重用对象
让我们遵循一些编程原则,DRY (不要重复自己),而不是创建具有类似功能的新对象,我们可以将它们抽象化大大地。在这个例子中,我将重用一个事件处理程序(再次使用事件)
//the usual way
function onShowContainer(e) {
$("#container").show();
}
function onHideContainer(e) {
$("#container").hide();
}
$("#btn1").on("click.btn1", onShowContainer);
$("#btn2").on("click.btn2", onHideContainer);
//the good way, passing data to events
function onToggleContainer(e) {
$("#container").toggle(e.data.show);
}
$("#btn1").on("click.btn1", { show: true }, onToggleContainer);
$("#btn2").on("click.btn2", { show: false }, onToggleContainer);
还有很多方法可以改进我们的代码,对性能产生影响,并防止内存泄漏。在这篇文章中,我主要讨论了事件,但还有其他可能导致内存泄漏的方式。我建议阅读之前发布的文章。
阅读愉快,编码愉快!