如果按特定顺序调用,则回调中的“this"通过应用变为未定义而绑定为闭包 [英] '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
,这个
实际上在严格模式下变成了 undefined,在非严格模式下变成了窗口;第二次点击.当我反其道而行之时,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"通过应用变为未定义而绑定为闭包的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!