简体   繁体   中英

How do functions bound by event listeners inherit `this` object

Pure Javascript only please. - also I'm pretty noob, so sorry if my question is a little convoluted.

I'm operating on an htmlCollection var puzzleAreaContents - which contains 16 <div> tags

next we go inside a loop, cycling through the elements, adding a click event listener.

puzzleAreaContents[movables[i]].addEventListener("click", shiftPuzzlePiece);

when I click on the element, I have access to this inside the shiftPuzzlePiece function, "this" being the the <div> tag I just clicked on.

I have two questions

  1. how / why does shiftPuzzlePiece function have access to this , the clicked dom element ?
  2. how can I pass any arbitrary element to shiftPuzzlePiece without breaking it's current usability? -- How can I define this when I'm passing the function an object, so that it behaves the same or similarly to when it is called via the click event listener?

that is to say that it is not currently set up to receive arguments

ex: shiftPuzzlePiece(some_arg)

  1. The event handler creates a new Execution Context ECMA which binds this to the element clicked
  2. You can use your own binding in order to replace this with call MDN .

For example,

shiftPuzzlePiece.call(puzzleAreaContents[movables[i]]);

Usually you call a function as a statement/expression:

var ret = shiftPuzzlePiece(arg0);

There are also Function.prototype.call and .apply , with which you can provide the this context:

var ret = shiftPuzzlePiece.call(that, arg0);
var ret = shiftPuzzlePiece.apply(that, [ arg0 ]);

Now that becomes this inside the function.


Example

var piece = { };

puzzleAreaContents[movables[i]].addEventListener("click", function () {

    shiftPuzzlePiece.call(piece, this /* element */);

});

function shiftPuzzlePiece(element) {

   // this === piece
   // element === the clicked puzzleAreaContents

}

You can bind any value to be the this object by using bind . For example:

shiftPuzzlePiece.bind({x:23});

Will ensure that the this object will have this.x equal to 23 .

You can also give pass in other parameters as well, but they would have to be in order. Bind returns a function.

More info about bind here .

The call and apply functions work similarly, but they do not return a new function but instead call the function.

Another way not mentioned here is using a closure which actually is faster than apply() , call() and bind()

(function f(){
  this.a = 0;
  var self = this;
  var e = document.getElementById('list').children, i;
  for (i = 0; i < e.length; i++) {
    (function(i){
      e[i].onclick = function(){
        _f(this, i);
      };
    })(i);
  }
})();

Or, it may be written like this as well

(function f(){
  this.a = 0;
  var self = this;
  var e = document.getElementById('list').children, i;
  for (i = 0; i < e.length; i++) {
    e[i].onclick = (function(i){
      return function() {
        _f(this, i);
      };
    })(i);
  }
})();

And finally, the _f function in this case

function _f(y, z){
  console.log(this.a + " / " + y.innerHTML + " / " + z);
}

HTML

<ul id="list">
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
</ul>

Output:

0 / 1 / 0
0 / 2 / 1
0 / 3 / 2
0 / 4 / 3
0 / 5 / 4

Working jsBin | Banchmark

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM