Reputation: 63
I have a simple class in JavaScript:
function MyCounter() {
this.counter = 0;
$('#button').click(this.run);
}
MyCounter.prototype.run = function() {
this.counter += 1;
return console.log(this.counter);
};
This class is invoked like that:
var myCounter = new MyCounter();
HTML includes a single clickable button with ID="button". Clicking this button is supposed to increment an internal variable inside a myCounter
instance. Obviously, it fails because this.counter
does not exist, because at the time on execution of bound handler this
equals to event, not myCounter
instance.
A crude hack to overcome this is to save "this" to some other variable and wrap calling actual handler into a anonymous function:
function MyCounter() {
this.counter = 0;
var this2 = this;
$('#button').click(function() {
this2.run();
});
}
Is there a better, cleaner way? Or at least, may be there's an universal agreement / style guide on how to name such "temporary this" variables?
Upvotes: 3
Views: 533
Reputation: 4654
This isn't really considered crude in javascript. One common way of doing something like this would be:
function MyCounter() {
this.counter = 0;
var self = this;
this.run = function() {
self.counter += 1;
console.log(self.counter);
};
$('#button').click(this.run);
}
Using the es6 syntax a common pattern would be to bind the function in the constructor:
class MyCounter {
constructor() {
this.counter = 0;
this.run = this.run.bind(this);
$('#button').on('click', this.run);
}
run() {
this.counter += 1;
console.log(this.counter);
}
// And now you can also remove the event listener:
destroy() {
$('#button').off('click', this.run);
}
}
But this pattern could also be applied to the old way of creating js classes:
function MyCounter() {
this.counter = 0;
this.run = this.run.bind(this);
$('#button').on('click', this.run);
}
MyCounter.prototype.run = function() {
this.counter += 1;
console.log(this.counter);
}
// And again you can also remove the event listener:
MyCounter.prototype.destroy = function() {
$('#button').off('click', this.run);
}
This is nice because you can now access the run
method from the prototype before initiating the class.
But I think the bottom line here is that you can't bind something to the class instance before it has been created.
Side note: In all of my examples, the run
method is bound to the class instance when it's initiated. This means that the run
method does not need to be bound again i.e. $('#button').click(this.run.bind(this));
.
var myCounter = (function() {
var counter = 0
function run() {
counter += 1
console.log(counter)
}
$('#button').on('click', run)
return {
run: run,
destroy: function() {
$('#button').off('click', run)
}
}
})()
Since there is only one instance of #button
, there is no reason to have the option of multiple class instances.
Upvotes: 1
Reputation: 2155
I'm not sure what it is, but I'm just not fond of the bind
function. As an alternative, you can scope this
to the object instance by wrapping it in an anonymous function, then use HTML 5's onclick
property to bind the function to the template.
var MyCounter = (function () {
function MyCounter(counter) {
this.counter = counter || 0;
}
MyCounter.prototype.run = function () {
return alert(this.counter += 1);
};
return MyCounter;
}());
var counterInstance = new MyCounter(10);
window.counterInstance = counterInstance;
...
<button onclick="counterInstance.run()">Test</button>
Another strategy - a more OO approach - would be to pass the element reference to the class itself and assign the method in the class constructor. That would look something like:
class Counter {
constructor(elementRef, counter = 0) {
this.element = elementRef;
this.counter = counter;
//native HTML type
this.element.onclick = () => this.run();
//alternatively
this.element.addEventListener('click', this.run);
}
function run() => { return this.counter++; }
}
Upvotes: -1
Reputation: 707456
You can just use .bind()
:
$('#button').click(this.run.bind(this));
.bind()
creates a mini stub function that essentially does this for you:
var self = this;
$('#button').click(function() {
return self.run();
});
Upvotes: 2