Javascript “的价值”;这";在使用addEventListener的处理程序中
我已经通过原型创建了一个Javascript对象。我正在尝试动态呈现一个表。虽然呈现部分很简单且工作正常,但我还需要为动态呈现的表处理某些客户端事件。那也很容易。我遇到的问题是处理事件的函数内部的“this”引用。不是“this”引用对象,而是引用引发事件的元素 参见代码。问题区域位于Javascript “的价值”;这";在使用addEventListener的处理程序中,javascript,oop,dom-events,Javascript,Oop,Dom Events,我已经通过原型创建了一个Javascript对象。我正在尝试动态呈现一个表。虽然呈现部分很简单且工作正常,但我还需要为动态呈现的表处理某些客户端事件。那也很容易。我遇到的问题是处理事件的函数内部的“this”引用。不是“this”引用对象,而是引用引发事件的元素 参见代码。问题区域位于ticketTable.prototype.handleCellClick=function(): function ticketTable(ticks) { // tickets is an array
ticketTable.prototype.handleCellClick=function()
:
function ticketTable(ticks)
{
// tickets is an array
this.tickets = ticks;
}
ticketTable.prototype.render = function(element)
{
var tbl = document.createElement("table");
for ( var i = 0; i < this.tickets.length; i++ )
{
// create row and cells
var row = document.createElement("tr");
var cell1 = document.createElement("td");
var cell2 = document.createElement("td");
// add text to the cells
cell1.appendChild(document.createTextNode(i));
cell2.appendChild(document.createTextNode(this.tickets[i]));
// handle clicks to the first cell.
// FYI, this only works in FF, need a little more code for IE
cell1.addEventListener("click", this.handleCellClick, false);
// add cells to row
row.appendChild(cell1);
row.appendChild(cell2);
// add row to table
tbl.appendChild(row);
}
// Add table to the page
element.appendChild(tbl);
}
ticketTable.prototype.handleCellClick = function()
{
// PROBLEM!!! in the context of this function,
// when used to handle an event,
// "this" is the element that triggered the event.
// this works fine
alert(this.innerHTML);
// this does not. I can't seem to figure out the syntax to access the array in the object.
alert(this.tickets.length);
}
功能票证(滴答声)
{
//票是一个数组
this.tickets=滴答声;
}
ticketTable.prototype.render=函数(元素)
{
var tbl=document.createElement(“表”);
for(var i=0;i
您需要将处理程序“绑定”到实例
var _this = this;
function onClickBound(e) {
_this.handleCellClick.call(cell1, e || window.event);
}
if (cell1.addEventListener) {
cell1.addEventListener("click", onClickBound, false);
}
else if (cell1.attachEvent) {
cell1.attachEvent("onclick", onClickBound);
}
请注意,这里的事件处理程序规范化事件
对象(作为第一个参数传递),并在适当的上下文中调用handleCellClick
(即引用附加到事件侦听器的元素)
还请注意,此处的上下文规范化(即在事件处理程序中设置适当的this
)会在用作事件处理程序(onClickBound
)的函数和元素对象(cell1
)之间创建循环引用。在某些版本的IE(6和7)中,这可能会导致内存泄漏。该漏洞本质上是由于本机对象和主机对象之间存在循环引用,导致浏览器无法在页面刷新时释放内存
为了避免这种情况,您需要a)删除这个规范化;b) 采用替代的(更复杂的)规范化策略;c) “清理”页面卸载上的现有事件侦听器,即通过使用removeEventListener
、detachEvent
和元素null
ing(不幸的是,这会使浏览器的快速历史导航无效)
您还可以找到一个JS库来处理这个问题。它们中的大多数(例如:jQuery、Prototype.js、YUI等)通常按照(c)中的描述处理清理。我知道这是一篇较老的文章,但您也可以简单地将上下文分配给变量self
,将您的函数放入一个匿名函数,该函数使用调用您的函数。调用(self)
并在上下文中传递
ticketTable.prototype.render = function(element) {
...
var self = this;
cell1.addEventListener('click', function(evt) { self.handleCellClick.call(self, evt) }, false);
...
};
这比“接受答案”更有效,因为上下文不需要为整个类或全局分配变量,而是整齐地隐藏在侦听事件的同一方法中。此外,还有一种方法是使用(来自DOM2!!我想知道为什么没有人提到它,因为这是最整洁的方式,适合这种情况。)
也就是说,不是传递回调函数,而是传递一个实现EventListener接口的对象,它指向事件处理程序函数。这里的主要区别是,在函数内部,this
将引用传递给addEventListener
的对象。也就是说,this。ticketTable
将是下面代码中的对象实例。要理解我的意思,请仔细查看修改后的代码:
ticketTable.prototype.render = function(element) {
...
var self = this;
/*
* Notice that Instead of a function, we pass an object.
* It has "handleEvent" property/key. You can add other
* objects inside the object. The whole object will become
* "this" when the function gets called.
*/
cell1.addEventListener('click', {
handleEvent:this.handleCellClick,
theTicketTable:this
}, false);
...
};
// note the "event" parameter added.
ticketTable.prototype.handleCellClick = function(event)
{
/*
* "this" does not always refer to the event target element.
* It is a bad practice to use 'this' to refer to event targets
* inside event handlers. Always use event.target or some property
* from 'event' object passed as parameter by the DOM engine.
*/
alert(event.target.innerHTML);
// "this" now points to the object we passed to addEventListener. So:
alert(this.theTicketTable.tickets.length);
}
您可以使用它来指定对给定函数的所有调用都应使用的值
var Something = function(element) {
this.name = 'Something Good';
this.onclick1 = function(event) {
console.log(this.name); // undefined, as this is the element
};
this.onclick2 = function(event) {
console.log(this.name); // 'Something Good', as this is the binded Something object
};
element.addEventListener('click', this.onclick1, false);
element.addEventListener('click', this.onclick2.bind(this), false); // Trick
}
上面示例中的一个问题是无法使用bind删除侦听器。另一个解决方案是使用名为handleEvent的特殊函数捕获任何事件:
var Something = function(element) {
this.name = 'Something Good';
this.handleEvent = function(event) {
console.log(this.name); // 'Something Good', as this is the Something object
switch(event.type) {
case 'click':
// some code here...
break;
case 'dblclick':
// some code here...
break;
}
};
// Note that the listeners in this case are this, not this.handleEvent
element.addEventListener('click', this, false);
element.addEventListener('dblclick', this, false);
// You can properly remove the listners
element.removeEventListener('click', this, false);
element.removeEventListener('dblclick', this, false);
}
喜欢总是最好的:)。我只是复制粘贴了这个部分,而不是回答这个问题。深受卡玛特伦和加加林回答的影响,我想我可能会解决这个问题
我在想,如果您将handeCellClick放在回调列表中,并使用事件上使用EventListener接口的对象触发回调列表方法,您可能会获得更多的自由
function ticketTable(ticks)
{
// tickets is an array
this.tickets = ticks;
// the callback array of methods to be run when
// event is triggered
this._callbacks = {handleCellClick:[this._handleCellClick]};
// assigned eventListenerInterface to one of this
// objects properties
this.handleCellClick = new eventListenerInterface(this,'handleCellClick');
}
//set when eventListenerInterface is instantiated
function eventListenerInterface(parent, callback_type)
{
this.parent = parent;
this.callback_type = callback_type;
}
//run when event is triggered
eventListenerInterface.prototype.handleEvent(evt)
{
for ( var i = 0; i < this.parent._callbacks[this.callback_type].length; i++ ) {
//run the callback method here, with this.parent as
//this and evt as the first argument to the method
this.parent._callbacks[this.callback_type][i].call(this.parent, evt);
}
}
ticketTable.prototype.render = function(element)
{
/* your code*/
{
/* your code*/
//the way the event is attached looks the same
cell1.addEventListener("click", this.handleCellClick, false);
/* your code*/
}
/* your code*/
}
//handleCellClick renamed to _handleCellClick
//and added evt attribute
ticketTable.prototype._handleCellClick = function(evt)
{
// this shouldn't work
alert(this.innerHTML);
// this however might work
alert(evt.target.innerHTML);
// this should work
alert(this.tickets.length);
}
功能票证(滴答声)
{
//票是一个数组
this.tickets=滴答声;
//要在以下情况下运行的方法的回调数组:
//事件被触发
this.\u callbacks={handleCellClick:[this.\u handleCellClick]};
//已将eventListenerInterface分配给其中一个
//对象属性
this.handleCellClick=新的eventListenerInterface(this,'handleCellClick');
}
//在实例化eventListenerInterface时设置
函数eventListenerInterface(父级,回调类型)
{
this.parent=parent;
this.callback\u type=callback\u type;
}
//在触发事件时运行
...
cell1.addEventListener("click", this.handleCellClick.bind(this));
...
ticketTable.prototype.handleCellClick = function(e)
{
alert(e.currentTarget.innerHTML);
alert(this.tickets.length);
}
var something = function(element) {
this.name = 'Something Good';
this.onclick1 = function(event) {
console.log(this.name); // 'Something Good'
};
element.addEventListener('click', () => this.onclick1());
}
document.addEventListener('click', (event) => {
// do stuff with event
// do stuff with this
});