如果按特定顺序调用,则回调中的"this"通过apply绑定为未定义 [英] 'this' in callback bound as closure via apply becoming undefined if called in specific order
问题描述
我有一个JS函数,我尝试在其中使用 this
,这应该是单击触发该函数的按钮:
I have a JS function where I try to use this
which is supposed to be the button clicked to trigger the function:
function myAction( greeting ) { console.log( greeting + ", the button is :" + this ); this.remove(); }
然后我的页面上有几个按钮,比方说两个(实际上甚至只有页面上的两个按钮;我已经对其进行了测试):
I then have several buttons on my page, let's say two (this really even happens with only two buttons on the page; I've tested it):
<button id="button-one" class="click-me">Click Me</button>
<button id="button-two" class="click-me">Click Me</button>
根据我的经验,当我想使用事件侦听器回调时,需要将相关元素用作 this
以及其他函数参数;如果我们有几个按钮共享,例如同一个类;达到此目的的最佳方法是将该回调绑定为闭包(将迭代的元素捕获为 this
+ apply(传递有关的 this
元素):
From my experience, when I want to use event listener callbacks which require the use of the concerned element as this
plus other function arguments; if we have several buttons sharing for example the same class; the best way to reach this is to bind that callback as a closure (to capture the iterated element as this
+ apply (to deliver the concerned this
element):
var myButtons = document.getElementsByClassName( "click-me" );
var amountOfButtons = myButtons.length;
for ( var i = 0; i < amountOfButtons; i++ ) {
myButtons[i].addEventListener( "click", (
function(i) {
return function() { myAction.apply( myButtons[i], ["hello"] ); };
}(i))
);
}
我从来没有对这种方法有任何疑问;它总是有效的.真的是错误的方法吗?
I never had any problem with this approach; it always worked. Is it actually the wrong approach?
因为,现在我要面对的是,当我单击#button-one
,然后单击#button-two
, this
实际上在严格模式下变为未定义,而窗口在非严格模式下变为;在第二次点击.相反,当我这样做时, this
始终保持为相应单击的按钮.它似乎以某种方式更新了 myButtons
集合,这样当通过第一次调用将第一个按钮删除时, 1
索引在该集合中变得不可用,因为仅剩一个按钮;另一个被删除.至少那是我的理论.但是在类似的代码案例中,我从未遇到过这个问题吗?那我该如何编码我想发生的事情?
Because, what I'm facing now is that, when I click on #button-one
, and then click on #button-two
, this
actually becomes undefined in strict mode and the window in non-strict mode; on the second click. When I do it the other way around, this
always sticks to be the accordingly clicked button. It somehow seems to update the myButtons
collection, such that when the first button gets removed via the first call, the 1
index becomes unavailable in the collection, as there's only one remaining button; the other one being removed. At least that's my theory. But I've never faced this issue before in similar code cases? How shall I code what I want to happen then?
编辑
谢谢大家;还要特别感谢@Nenad Vracar,因为他的解决方案只需更改以下内容,就可以实现所需的最低限度的编辑,而这些编辑完全可以正常工作:
Thanks to all of you; and special thanks to @Nenad Vracar, as his solution led to the minimum needed edits which perfectly work, by simply changing:
var myButtons = document.getElementsByClassName( "click-me" );
var amountOfButtons = myButtons.length;
for ( var i = 0; i < amountOfButtons; i++ ) {
myButtons[i].addEventListener( "click", (
function(i) {
return function() { myAction.apply( myButtons[i], ["hello"] ); };
}(i))
);
}
收件人:
var myButtons = document.getElementsByClassName( "click-me" );
var amountOfButtons = myButtons.length;
for ( var i = 0; i < amountOfButtons; i++ ) {
myButtons[i].addEventListener( "click", function() {
myAction.apply( this, ["hello"] );
});
}
一个简单的智能解决方案,我从字面上看不到树木的木头!再次感谢!
Simple smart solution, I could literally not see the wood for the trees! thanks again!
注意.事件委托在这里并不是真正的选择,因为页面的DOM结构/需要附件的元素有些复杂,我通常更喜欢将函数附加到需要它的元素上.另外,我更喜欢不要将函数的代码更改为闭包本身,也不要更改函数的代码本身.因此,我决定使用此解决方案的原因.
Note. Event delegation was not really an option here, as the DOM structure of the page / the elements needing the attachment is somewhat complicated, and I generally prefer to attach the function to the elements needing it. Also, I prefer to not change the code of the function to be a closure itself, to not make changes to the function code itself. Hence the reason why I've decided to use this solution.
推荐答案
在使用 querySelectorAll
然后可以在其上使用 forEach
循环时,可以使用稍微不同的方法.在点击事件处理程序内,您不需要多余的闭包,而在 myAction
内部的 this
将是点击事件处理程序内的 this
您将它作为 thisArg
参数传递给 apply
.
You could use slightly different approach where you use querySelectorAll
and then you can use forEach
loop on that. And inside the click event handler you don't need that extra closure and this
inside the myAction
will be the this
inside the click event handler since you passed it as thisArg
parameter to apply
.
function myAction(greeting) {
console.log(greeting + ", the button is :" + this.textContent);
this.remove();
}
document
.querySelectorAll(".click-me")
.forEach(function(btn) {
btn.addEventListener('click', function() {
myAction.apply(this, ['Hello'])
})
})
<button id="button-one" class="click-me">One</button>
<button id="button-two" class="click-me">Two</button>
这篇关于如果按特定顺序调用,则回调中的"this"通过apply绑定为未定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!