Working with scope in object methods containing the keyword 'this' called event listeners

I have summarized my lack of understanding of the situation with this small problem. Here is what I think I know so far:

I have a myDog (global variable). Dog has an el member variable that is an html element; because it is an element, I can add event listeners to it. So, when you click myDog.el , it logs the values โ€‹โ€‹of this.name and myDog.name to the console. As expected due to volume, this.name is undefined and myDog.name is 'tye'. this inside Dog.speak when called when listening for click events refers to the element that was clicked, the member variable el , and not the Dog object. Since myDog is a global variable, it can select a backup regardless of scope and just myDog.name .

See the code below:

 function Dog(name,id) { this.name = name ? name : "spot"; this.id = id ? id : "dog"; this.el = document.getElementById(this.id); // given there is a div with a matching this.el.addEventListener("click",this.speak); // ignore IE for simplicity (attachEvent has its own 'this' scope issues) } Dog.prototype = { speak: function() { console.log("this.name: "+this.name+"\nmyDog.name: "+myDog.name); } }; var myDog = new Dog("tye","dog1"); 

So ... my questions are:

1) What strategies exist for binding objects to html elements, so that I can go from this.el to myDog ( this.el owner) without myDog being a global variable?

2) Are global variables in this case a necessary evil? And if so, what are some good strategies to use them gracefully? For example, what if I wanted to create 100 dogs? How would I handle all of these global variables in Dog.speak ?

Here's the jsfiddle version if you want to play with it: http://jsfiddle.net/chadhutchins/Ewgw5/

+4
source share
3 answers

"1) What strategies exist for binding objects to html elements ..."

Since you use .addEventListener() , I would suggest using a feature that few people know about ... your Dog object implements the EventListener interface.

This establishes a very clean relationship between your Dog data and its associated item.

Only minor changes are required. First code ... explanation below.


DEMO: http://jsfiddle.net/Ewgw5/1/

 function Dog(name,id) { this.name = name ? name : "spot"; this.id = id ? id : "dog"; this.el = document.getElementById(this.id); // ---------------------------------v----no function! this.el.addEventListener("click", this); } Dog.prototype = { // Implement the `EventListener` interface handleEvent: function(event) { switch (event.type) { case "click": return this.speak(); } }, speak: function() { console.log("this.name: "+this.name+"\nmyDog.name: "+myDog.name); } }; var myDog = new Dog("tye","dog1"); 

So, all I did was pass this in the constructor to addEventListener() instead of passing the function, and then I added the handleEvent() method to Dog.prototype .

Now, when the "click" event occurs, it calls the handleEvent() method. The value of this in this method will be your instance of Dog . Therefore, from there you can call any method (s) that you need.

Since you made the element the this property, you can access the element through this.el But this is technically not even necessary, since the element is also accessible through the event object as event.currentTarget .


"2) Are global variables in this case a necessary evil ..."

Fortunately not!


This behavior should be part of your padding for .addEventListener() .

+4
source

You can try something like this:

 function Dog(name,id) { var self = this; // <---- NEW BIT saving a reference to this this.name = name || "spot"; // <-- unrelated tidy-up to use || instead of ?: this.id = id || "dog"; this.el = document.getElementById(this.id); // given there is a div with a matching this.el.addEventListener("click",function(){ self.speak(); }); // ignore IE for simplicity (attachEvent has its own 'this' scope issues) } 

Through closure magic, the anonymous function that I added to addEventListener() has access to the scope of the contained function even after the return function returns, so it can use self , which contains a link to the original object stored with this when Dog() was called as constructor.

EDIT: Sorry, I did not directly address questions (1) and (2) you indicated, but as you can see, you do not need global variables to fix this problem. Using the technique that I described, you can create 100 dogs, and all of them will work. (Well, they all say: everyone will work if you add Dog.prototype.work = function() { } .)

+3
source

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind

  this.el.addEventListener("click", this.speak.bind(this)); 

This method is preferable since the selection of the area does not require allocation of the area. Area allocation is one of the most expensive things in JS.

+1
source

Source: https://habr.com/ru/post/1384041/


All Articles