Javascript Closures and 'this'

WHen the function is called, "this" refers to row. If you want to have the object, you can do it something like this: ]

AddChildRowEvents: function(row, p2) {
    var theObj = this;
    if(document.attachEvent) {
         row.attachEvent('onclick', function(){theObj.DoSomething();});
    } else {
         row.addEventListener('click', function(){theObj.DoSomething();}, false);
    }
},

When the function is called, it has access to the variable theOBj which was in scope when the function was defined.


this always refers to the inner function, if you have nested functions, you have to create another variable and point that to this.

var myObject = {
    AddChildRowEvents: function(row, p2) {
        var that = this;
        if(document.attachEvent) {
            row.attachEvent('onclick', function(){that.DoSomething();});
        } else {
            row.addEventListener('click', function(){that.DoSomething();}, false);
        }
    }
}

The problem come from how this is managed in JS, wich can be quickly, especially when you have callback called asynchronously (and when a closure is created, bound to this).

When the AddChildRowEvents method is called, the this references well the variable myObject. But the aim of this method is to put a handler for click. So the event will be triggered sometime in the future. At that time, what object will really trigger the event? In fact, it's the DOM element on which the user will click. So this will reference this DOM element and not the myObject variable.

For a more modern solution than those presented, one can use bind method or arrow function.

1. Solution with arrow function

let handler = {
	addEventHandler: function(row) {
	  console.log("this", this)
  	row.addEventListener("click", () => {
    	console.log("this", this)
    	this.doSomethingElse()
    })
  },
  
  doSomethingElse: function() {
  	console.log("something else")
  }
}
var div = document.querySelector("div")
handler.addEventHandler(div)
<div>one</div>

With arrow function (which are available since ES2015), the this context in not the execution context but the lexical context. The difference between the two is that lexical context is found at build time not at execution time, so the lexical context is easier to track. Here, inside the arrow function, this references the same this of the outside function (i. e. addEventHandler) so reference myObject.

For more explanation of the properties of fat arrow function vs. regular functions: https://dmitripavlutin.com/6-ways-to-declare-javascript-functions/

2. Solution with bind

let handler = {
	addEventHandler: function(row) {
	  console.log("this", this)
  	row.addEventListener("click", function() {
    	console.log("this", this)
    	this.doSomethingElse()
    }.bind(this))
  },
  
  doSomethingElse: function() {
  	console.log("something else")
  }
}
var div = document.querySelector("div")
handler.addEventHandler(div)
<div>one</div>

This time, when the callback function is passed to addEventListener, one have bound the this context to the this element in the outside addEventHandler function.


This is a common issue with closures. To resolve it try something like this:

var myObject = {    
    AddChildRowEvents: function(row, p2) { 
        var self = this;

        if(document.attachEvent) {            
             row.attachEvent('onclick', function(){this.DoSomething(self);});        
        } else {            
             row.addEventListener('click', function(){this.DoSomething(self);}, false);        
        }    
    },    

    DoSomething: function(self) {       
        self.SomethingElse(); 
    }
}