A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function.
In JavaScript, the this keyword refers to an object. Which object depends on how this is being invoked (used or called). The this keyword refers to different objects depending on how it is used: In an object method, this refers to the object.
In JavaScript, closures are the primary mechanism used to enable data privacy. When you use closures for data privacy, the enclosed variables are only in scope within the containing (outer) function. You can't get at the data from an outside scope except through the object's privileged methods.
Disadvantages of closures There are two main disadvantages of overusing closures: The variables declared inside a closure are not garbage collected. Too many closures can slow down your application. This is actually caused by duplication of code in the memory.
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
.
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/
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();
}
}
The problem with your code is here, in the following line of code you have defined an anonymous function and passed it as an event handler for the onClick event, in the following lines:
row.attachEvent('onclick', function(){this.DoSomething();});
and
row.addEventListener('click', function(){this.DoSomething();}, false);
when onclick event raised and it calls the anonymous function that you have passed, this context is referring to the object that has been raised the event, but not myObject. because at this point executing context is in the event handler context.
The solution: you can do the trick that Mike Kantor has been told, or using jQuery, use the proxy method, to define this context in the anonymous function.
So 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 that the error is in this.SomthingElse( ), but your code does not show it. If you really getting error at that line of code, it may you are using the method DoSomthing somewhere else as an event handler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With