This note's purpose is to solidify knowledge on how to construct and use different types of functions in JS.
Constructors are functions which allow to share behavior by their instances. They're basically like classes in Ruby.
var Party = function() {
this.status = undefined
};
var Game = function() {};
Each new instance of Party
will have a status. At the beginning it will be undefined.
console.log(party);
Party {status: undefined}
Each new instance of Game
will be an empty object having no attributes.
Once we have constructors, we can create (prototype) methods that will be available to use by the instances of those constructors. Below I will describe 3 types of functions according to the argument passed.
Game.prototype.playGame = function() {
console.log("GAME HAS STARTED");
}
The function above accepts no arguments (hence empty ()
). When called, it console logs a string.
var game = new Game();
game.playGame(); // console logs "GAME HAS STARTED"
Game.prototype.howManyStars = function() {
this.numberOfStars = countingStars();
}
var countingStars = function() {
return Math.floor((Math.random() * 100) + 1);
}
In this example we create a howManyStars
method to set the number of stars playing in the particular game. We pass no arguments, but inside of howManyStars
methods we use the output of countingStars()
function.
var game = new Game();
game.howManyStars(); // sets the numberOfStars to a random number from 0 to 100.
Game.prototype.duration = function(hours) {
this.duration = hours;
}
In the example above, the duration
function accepts one argument - hours
. This argument is intended to be a primitive (string, number or symbol). What is important, at this point I don't want this argument to be another function.
When called, this method will set the duration of a particular instance of Game constructor.
var game = new Game();
game.duration(5);
console.log(game.duration) // 5
JS (like for example Ruby) allows to pass functions as arguments in another functions.
We create two methods: beforeGame
and afterGame
available for the instances of Game
.
Game.prototype.beforeGame = function(callback) {
this.runBeforeGame = callback;
};
Game.prototype.afterGame = function(callback) {
this.runAfterGame = callback;
};
In case of both methods, I would like to set the attributes (runBeforeGame
and runAfterGame
) to be functions. Those functions haven't been yet created.
--> Why is it useful?
Thanks to this operation, all instances of class game will have user-defined functions available to them. For example we have three games: basketball, pingpong and chess. Each of them has the runBeforeGame attribute which can be specific to its needs. In case of basketball: clean the floor, in case of pingpong: set up the table, in case of chess: reset the clock.
Each time I will want them to run a method, it will be available to them as an attribute. At the end, I want to be able to call:
game.runBeforeGame();
and this way invoke a particular, predefined function.
Once we have prototyped methods that are able to set the attribute to the function, it's time to write those functions.
var basketball = new Game();
basketball.beforeGame(function() {
party = new Party();
party.status = "started";
console.log("Cleaning the floor");
console.log("pumping the ball")
});
var pingpong = new Game();
pingpong.beforeGame(function() {
party = new Party();
party.status = "started";
console.log("set up the table")
});
pingpong.afterGame(function() {
party.status = "ended";
console.log("The game is finished!")
});
- In both examples I call the methods on a particular instance of
Game
-basketball
orpingpong
. - I call the empty prototyped methods drafter in the previous step (Preparation) and fill them with functions.
3) In case of `beforeGame`, I want to set the `runBeforeGame` attribute to a function that:
- creates a new instance of `Party`;
- sets the `status` of this `party` to be `started`;
- console logs a specific message.
4) In case of `afterGame`, I want to set the `runAfterGame` attribute to a function that:
- changes the `status` of `party` to `ended`;
- console logs `The game is finished`.
- In this stage we're still not calling those functions. We have just prepared them .
Now it's time to use the methods drafted in the previous two steps.
We create and run a new function called gaming
that will run the whole sequence of relevant methods:
var gaming = function() {
game.runBeforeGame();
game.playGame();
game.runAfterGame();
}
gaming();
Let's analyze it step by step:
-
first,
gaming
function runs thegame.runBeforeGame()
; -
runBeforeGame
is an attribute of Game, which we set to a function when we calledgame.beforeGame
Game.prototype.beforeGame = function(callback) {
this.runBeforeGame = callback;
};
var game = new Game();
game.beforeGame(function() {
party = new Party();
party.status = "started";
console.log("New party was created!")
})
-
this way by typing only
game.runBeforeGame()
we run all bunch of predefined things at once. -
second,
gaming
function runs thegame.playGame()
that simply console logsGAME HAS STARTED
. -
third,
gaming
function runs thegame.runAfterGame()
that does all the things predefined when we calledgame.aftergame
Game.prototype.afterGame = function(callback) {
this.runAfterGame = callback;
};
game.afterGame(function() {
party.status = "ended";
console.log("The game is finished!")
});