如果按特定顺序调用,则回调中的“this"通过应用变为未定义而绑定为闭包 [英] 'this' in callback bound as closure via apply becoming undefined if called in specific order

查看:19
本文介绍了如果按特定顺序调用,则回调中的“this"通过应用变为未定义而绑定为闭包的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆