Hello,
I will be very grateful if someone could explain to me why both examples do what they do.
First case.
My first approach, which didnt’t yield expexted results .
const Tetris = function(name) {
this.name = name;
};
Tetris.prototype.moveDown = function() {
console.log(this); // gives {ArrowDown: ƒ}
console.log(this.name + " moved down!"); // gives 'undefined moved down!
};
const tetris = new Tetris('Square');
const keydownHandler = function() {
let targetObject = tetris;
const listenedKeys = {
ArrowDown: targetObject.moveDown,
};
Object.keys(listenedKeys).forEach((name) => {if(event.key === name) { listenedKeys[name]() } });
};
window.addEventListener('keydown', keydownHandler);
However if I wrap the tetris instance like this:
const tetrisWrapped = (function() {
let tetrisInstance = tetris;
function moveDown() {
tetrisInstance.moveDown()
}
return {
moveDown: moveDown
};
})();
and let targetObject in the keydownHandler be tetrisWrapped, then I get what I want: “this” is the Tetris constructor and its moveDown method knows what is doing.
I am glad that I found the way around, but I 'd prefer to know what I am doing. 
Thank you!
For one, you are not passing event into your event handler callback function. So the event parameter you are trying to access to compare the keyname doesn’t exist.
I definitely would not be satisfied with the state of whatever you’re trying to accomplish. I can’t quite understand why you have this setup this way.
In the first block of code, I see that listenedKeys
is created as a new Object literal with the property ArrowDown
assigned to the moveDown
function.
Okay, I can see where the confusion lies. You now have a listenedKeys
object defined as follows:
{
ArrowDown: function() {
console.log(this);
console.log(this.name);
}
}
Notice, however, that the this
of listenedKeys has no name
property, but it certainly does have a this
object with the ArrowDown
function property as defined in the declaration:
const listenedKeys = {
ArrowDown: targetObject.moveDown,
};
JavaScript treats functions as standalone when inserted outside of their native instance of this
. It is contrary to what you might expect this
to be mean based of classic object-oriented languages. Let’s not forget that JavaScript is as functional as it is object-oriented.
In your second block of code, you are creating a immediately invoke function expression wherein tetris
is reassigned as tetrisInstance
. Ok cool, so this
is preserved in the reassignment of tetris
to tetrisInstance
.
The function moveDown
of tetrisWrapped
invokes the moveDown
of the tetrisInstance
which does not have a moveDown
function of its down. Thankfully, it does have one on its prototype inherited from Tetris
which was originally instantiated through tetris
. It was then reassigned to tetrisInstance
in the moveDown
function of tetrisWrapped
.
This was a mouthful to explain, and I think it would help to watch this video followed by a further explanation on prototypes to get a better grasp of what you are dealing with here.
2 Likes
The event parameter does exist. Just take a look on the console:
const keydownHandler = function() {
console.log(event)
};
window.addEventListener('keydown', keydownHandler);
You can find a short explanation here:
“The handler function, by default, when executed is passed the event object (that was created when the event/action you are interested in happened) as an argument.”.
As for my satifaction for the “way around” , I was satisfied I found any. at all
I am aware that it’s probably not the best one. Maybe you will know any good resources on design patterns including adding event listeners and defining handlers,? I assume, that if you have different kind of browser events coming form user interaction (like in a tetris game I am building), together with events coming from the code, there must be a way of somehow grouping the callbacks together and then looping through them if necessary? Would be the pub/sub pattern suitable?
Thanks.
The event is passed into the function by default, but you still need to declare an event parameter name.
I’m getting this errror in the firefox console using the code you provided as it is.
ReferenceError: event is not defined
keydownHandler/< debugger eval code:15:1
forEach self-hosted:261:13
keydownHandler debugger eval code:15:3
IF you want to use the event
in keydownHandler
, declare that parameter. You can name the parameter whatever you want, because as you quoted, it is passed into the function by default from the function using it as a callback, but as a callback function it won’t have any reference to that event if you don’t do so. It appears that by default a callback is not designed to grab arguments[0]
if a user does not properly build their functions.
const keydownHandler = function( event )
Absolutely, it’s always the first step to get something working. I just hope you don’t grow to be satisfied with that. I’m speaking from experience.
As a general principle on design patterns when you see a definition for a function you must still imitate that definitions design. You did so when you used the forEach() method, the order of the parameters matters, if you don’t declare which parameters you want you won’t have access to that data from the method. The same is true with a callback for the addEventListener. https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#The_event_listener_callback
1 Like
As @sosmaniac-FCC explained, the listenedKeys is just storing the method in itself. I like to use string to store commands and bracket notation to access methods.
You could do this instead.
listenedKeys = {
ArrowDown: "moveDown"
}
if(event.key === name) {
targetObject[ listenedKeys[name] ]();
}
1 Like