A few things:
-
Most people will suggest something like
var self = this
because it’s fast and easy. -
But
var self = this
does not separate the view object entirely from the view logic, which coming from a more formal C# background and looking at your code, sounds like something you want to do. -
In order to have the callback execute only when the event fires, wrap the handler in a function, so that it’s evaluated right away, but only executed when and if a
keydown
event fires (see the code below). -
Understanding scope in JS: Whatever the execution context is, is also the current scope. Your listener was added in a method (called
listen
) onKeyboard.prototype
, but thekeydown
event is actually fired onwindow
— the handler is executing in a different context than where it was defined; it’s executing within the context of what is invoking it, in this case,window
, so it’s scoped towindow
unless you bind it to another object viabind
orapply
when it’s defined.
In your code, window
is the view a user’s interacting with, and Keyboard
is that view’s controller. In MVC patterns like what you’re probably used to in C#/.NET, views don’t tell themselves what to do when things happen, controllers tell views what to do. So, if you were to assign a reference to the controller by using var self = this
like so many do, the view would be managing itself — but only for that specific handler for keydown
events. This is inconsistent and would become hard to manage in a large project.
A solution:
Keyboard.prototype.listen = function() {
window.addEventListener('keydown', function(e) {
this.handle_keydown(e);
}.bind(this), false);
}
A better solution:
Keyboard.prototype.view = window;
Keyboard.prototype.listen = function() {
this.view.addEventListener('keydown', function(e) {
this.handle_keydown(e);
}.bind(this), false);
}
The best solution (until ES6 class
is ready):
// define
function addViewController(view) {
function ViewController() {
this.handle_keydown = function(args) {
// handle keydown events
};
this.listen = function() {
this.view.addEventListener('keydown', function(e) {
this.handle_keydown(e);
}.bind(this), false);
};
this.view = view;
return this;
}
return new ViewController(view);
}
// implement
var keyboard = addViewController(window);
keyboard.listen();
- Note:
.bind()
is compatible with ECMAScript 5+; if you need a solution for older browsers, Mozilla has posted a great alternative to.bind()
usingfunctions
and.call()
:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
Edit: Here’s what your instantiated keyboard
object will look like using this new, modular solution: