[ACCEPTED]-Javascript Closures and 'this'-closures

Accepted answer
Score: 39

WHen the function is called, "this" refers 5 to row. If you want to have the object, you 4 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 3 function is called, it has access to the 2 variable theOBj which was in scope when 1 the function was defined.

Score: 12

this always refers to the inner function, if 2 you have nested functions, you have to create 1 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);
        }
    }
}
Score: 9

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

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

For a more modern solution 14 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 13 arrow function (which are available since ES2015), the this context 12 in not the execution context but the lexical 11 context. The difference between the two 10 is that lexical context is found at build 9 time not at execution time, so the lexical 8 context is easier to track. Here, inside 7 the arrow function, this references the same this of the outside 6 function (i. e. addEventHandler) so reference myObject.

For more 5 explanation of the properties of fat arrow 4 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 3 the callback function is passed to addEventListener, one 2 have bound the this context to the this element 1 in the outside addEventHandler function.

Score: 3

This is a common issue with closures. To 1 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(); 
    }
}
Score: 2

The problem with your code is here, in the 17 following line of code you have defined 16 an anonymous function and passed it as an 15 event handler for the onClick event, in 14 the following lines:

    row.attachEvent('onclick', function(){this.DoSomething();});

and

    row.addEventListener('click', function(){this.DoSomething();}, false);

when onclick event 13 raised and it calls the anonymous function 12 that you have passed, this context is referring 11 to the object that has been raised the event, but 10 not myObject. because at this point executing context 9 is in the event handler context.

The solution: you 8 can do the trick that Mike Kantor has been 7 told, or using jQuery, use the proxy method, to 6 define this context in the anonymous function.

So 5 your code would be like this:

var myObject = {

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

    DoSomething: function() {
        this.SomethingElse(); //<-- Error here, object 'this' does not support this method.
    }
}

you have mentioned 4 that the error is in this.SomthingElse( ), but your code does 3 not show it. If you really getting error 2 at that line of code, it may you are using 1 the method DoSomthing somewhere else as an event handler.

More Related questions