- compare and contrast objects in Ruby and Javascript
- learn to use constructor functions to create objects
- understand how to attach attributes and methods to an object via
prototype
property
- What is an object?
- Why do we use the OOP paradigm? What benefits does it provide us?
An object is a self-contained entity that consists of both data and procedures to manipulate that data. In a nutshell, object-oriented programming lets us model data as nouns, and their associated behaviors as verbs. It's an intuitive way for programmers to structure code.
Let's recall in Ruby how we made objects at a high level.
class Person
def initialize (name, age)
@name = name
@age = age
end
def introduce
puts "Hi, I'm #{@name}, and I'm #{@age} years old!"
end
end
ash = new Person("Ash", 10)
ash.introduce # "Hi, I'm Ash, and I'm 10 years old!"
And now - in Javascript, let's make an object:
var object = {}
...no, but really:
var ash = {
name: "Ash",
age: "10",
introduce: function(){
console.log("Hi, I'm " + this.name + " and I'm " + this.age + "years old!")
}
}
ash.introduce(); // "Hi, I'm Ash, and I'm 10 years old!"
While we were learning Ruby, we often made the analogy that classes are the 'blueprints' for an object, and the instances are individual occurrences of that object. Instantiating multiple objects is as simple as calling the class function as many times as you need to. But, Javascript doesn't have classes!
Hard-coding each individual object that we want would be fairly inefficient. What if we used a function to create these objects? We would end up with something like this:
var makePerson = function(name, age){
var newPerson = {};
newPerson.name = name;
newPerson.age = age;
newPerson.introduce = function(){
console.log("Hi, I'm " + this.name + " and I'm " + this.age + "years old!")
}
return newPerson;
}
var ash = makePerson("Ash", "10");
ash.introduce(); //"Hi, I'm Ash, and I'm 10 years old!"
It turns out JS already comes with some tools for us to do this...
In JS, we use Constructor Functions to accomplish similar results. I'll build our first Constructor function here...
function Person(name, age){
this.name = name;
this.age = age;
this.introduce = function(){
console.log("Hi, I'm " + this.name + " and I'm " + this.age + " years old!");
};
}
var ash = new Person("Ash", 10);
ash.introduce();
A few things to note here:
- The capital P in
Person
. This is a convention to indicate that you're using a constructor function to create objects, much like how you'd do the same in Ruby with class names. - The words
this
andnew
. How does the constructor function know whatthis
is? It's because in using the wordnew
, JS knows to create a new empty object, set it as the target ofthis
, and return the object once it's done assigning properties. - I said this wasn't creating a class, but it sure awfully looks similar! That is the point of why this syntax exists, but under the hood, Javascript is doing something slightly different - which we'll get into a bit later.
Let's write a constructor function for a Pokemon that takes two arguments name
and move
, and has a method attack
that prints what move the Pokemon used to the console. Then, call it to create a new Pokemon. (If you don't know/can't remember anything about Pokemon, Pikachu
knows to use Thundershock
at Level 1.)
function Pokemon(name, move){
//code here...
}
Each time we call new Person
, our constructor function creates a new object with all of the attributes we defined. But, given that there's common behavior across these objects, we can further abstract some of this code out of our constructor function. Enter the prototype
property:
function Pokemon(name, move){
this.name = name;
this.move = move;
}
Pokemon.prototype.attack = function(){
console.log(name + " used " + move + "! It's super effective!");
};
var pikachu = new Pokemon("Pikachu", "Thunderbolt");
pikachu.attack();
What's actually going on here? There are actually a couple more things that we didn't mention when we talked about invoking the constructor function.
In Javascript, each object has a prototype
property that references another object - and along with it come associated methods and attributes. When we created the pikachu
object by invoking new Pokemon
, we also assigned it all the properties and methods that were found on Pokemon.prototype
. Whenever we tell Pikachu to attack, the object looks for the property on the pikachu
object - and if it can't find it, it'll go to the prototype to see if it's there instead.
This is a dynamic way to add new methods to 'classes' of objects in Javascript at runtime - something that's a bit more complicated in other languages.
pikachu.introduce(); // returns an error
Pokemon.prototype.introduce = function(){
console.log(this.name + "!")
}
pikachu.introduce(); // returns "Pikachu!"
In the world of Pokemon, people challenge each other to Pokemon battles all the time. Rewrite our Person constructor using the prototype
property so that the Person can also challenge
, win
, and lose
, each with its' own console.log statement.
BONUS: Add a money
attribute for each Person, and write the logic for the person to win or lose money in each corresponding method. Be careful of how you assign the money
attribute!
- Dangers and pitfalls in overwriting prototypes
- The
__proto__
property - Prototypal inheritance (Trainer that inherits from a Person)
- Emulating public, private and privileged methods