Let's review methods
Methods are messages that we send from Object
s to other Objects
. Let's start our review with looking at a familiar method of the String
class. When we want to compare a String
with another, regardless of case, one of our approaches is to use the toLowerCase
method.
Let's say that we have a String
variable named color
:
if (color.toLowerCase() === "the colour out of space") {
System.out.println("That is truly a strange color.");
}
Calling color.toLowerCase()
is like saying, "Hey, color
, can you give me a lowercase version of yourself?" Whether color
holds the value "Purple"
, "purple"
, or "PuRpLe"
, it will respond "purple"
.
We refer to a method's response as it returning a value.
Information, Please
When a method needs additional information, we add method parameters to its definition.
Remember the split
method of String
? We passed it a String
, indicating what we wanted to use as a delimiter to split it:
let str = "this, that, the other";
let pieces = str.split(", ");
console.log(pieces[1]); // prints "that"
This is the declaration of the split
method. We say that it accepts a method parameter of type String
, named separator
:
String.prototype.split(separator)
If we didn't pass it the separator
parameter, how would it know where to split?
John Hancock
The combination of the method name and parameter types is called its method signature. This is how JavaScript identifies which method we are calling. Also, the method signature allows JavaScript to determine whether a method is redefining another method (called overriding):
String.prototype.split(String regex)
Prototype
What is the prototype
part in the middle? well JavaScript is a prototypal inheritance language. This means that when we inherit behavior from an Object
we will inherit any methods that exist on the parent prototype.
Talking Back
Not all methods return
something, but if they do return
a response they must use the return
statement.
A String
is an abstraction representing a sequence of characters, so it contains an instance variable containing those characters. Lets look at how its replace
method works:
String.prototype.replace(substr, newSubstr);
which we can call like this:
let str = new String("My name is Brian.");
str.replace("Brian", "Donny");
console.log(str); // prints: My name is Donny
Talking Back (continued)
String.prototype.replace(substr, newSubstr);
In order to replace a value in a String
, a String
looks at value
and parses it looking for the specified substring (substr) and replaces it with a new substring (newSubstr).
Creating An Object
Object Oriented Programming is all about creating objects (instances of an Object
) and communicating with those objects (calling methods). Objects contain instance variables that contain the state of an object, describing the object's attributes.
Let's say that we have a simple Parrot
class that looks like this:
class Parrot {}
So, far when we have created an object and defined its state, we've done it something like this:
const myParrot = new Parrot();
myParrot.name = "Dewd";
Here, we've created an instance of the Parrot
class named myParrot
. myParrot
has an instance variable named name
with the value "Dewd"
.
A Flock of Parrots
San Francisco is just the right climate for parrots from Central/South America. Flocks of parrots that have escaped from owners or been let loose flock together on Telegraph Hill. Birds of a feather do truly flock together.
I recommend tracking down a flock sometime. They're wild (err... feral).
Creating lots of objects
Let's say we need to create a lot of parrots. There's a flock. That wouldn't be too difficult, given that our parrots only have a name right now, but if we started adding other attributes, this would become more and more difficult. We're developers, not typists. Also, it would be easy to forget to assign one or more attributes. So we can make it easier and less error prone by creating a method to create Parrot
instances.
In Practice
Here's what we were doing before:
const myParrot = new Parrot();
myParrot.name = "Dewd";
Here's how we could create a method to do the same thing:
function createParrot(parrotName) {
const p = new Parrot();
p.name = parrotName;
return p;
}
And here's how we would call it:
const myParrot = createParrot("Dewd");
But that still seems like a lot of work?
Luckily, there's a cleaner way. Remember that constructor we've been calling?
const p = new Parrot();
We call this a constructor because it constructs an instance of the class.
But there's no constructor in my class!
This constructor is what is known as a default constructor. If we haven't explicitly defined a constructor for our class, the runtime environment creates a constructor for us that doesn't accept any arguments and does basic instance construction. That's why we don't see the constructor in our class.
The constructor that the browser nicely creates for us if we have not explicitly declared one looks like this:
constructor() {
}
Note that it has no return
statement and we call it with the convenient name constructor
.
We have the technology
When we want to initialize attributes of an object, we create our own constructor that accepts parameters, just like methods that accept parameters.
Remember our createParrot
method?
function createParrot(String parrotName) {
const p = new Parrot();
p.name = parrotName;
return p;
}
We can (and should) move this work into the constructor for the class. Here is our Parrot
class with an equivalent constructor:
class Parrot {
constructor(parrotName) {
this.name = parrotName; // saying this.name will create an instance variable for us
}
}
We can construct it
Once we've created our constructor, instead of this:
const myParrot = createParrot("Dewd");
we can do this:
const myParrot = new Parrot("Dewd");
This is cleaner, more expressive, and moves the responsibility of construction to the Parrot
class, where it belongs.