By the end of this lesson, students should be able to:
- Demonstrate a use case that explains prototypal inheritance and what kind of flexibility it gives to programmers
- Use namespaces to organize application code
- Define a custom constructor method that sets one or more properties of a new object
Before this lesson, students should already:
- Have a basic understanding of Javascript concepts, including functions and objects
- Have a basic understanding of OOP in the context of Ruby
Classes in Javascript are defined a bit differently than in other languages. Rather than being defined explicitly with language-level keywords, they are expressed using only a constructor and an object called a "prototype".
If a traditional class definition is like a blueprint that new objects are constructed based on, a prototype is like a template that gets duplicated to serve as the starting point for a new object.
(demonstration of Person class definition, with name variable and introduction method)
As you can see, in Javascript defining a constructor is the same as defining a class. We can then add methods to that class' prototype.
A class allows us to define a category of objects, all of whose members have certain things in common. But what if we want to define a more specific subset of that category, which shares the parent category's attributes but also has some of its own? This is where inheritance comes in.
(demonstration of Student subclass)
So there's a lot going on here. Some of it is familiar, some of it isn't. The
main things you probably notice are Student.call()
and Object.create()
. Let's
walk through the code.
So, just as before, we're defining a constructor for our new class. This is all
Javascript needs to consider this a fully usable class. However, a Student is a type
of Person, and we don't want to rewrite the things the two have in common. So
before anything else we call Person's constructor, because it defines an Person's
attributes and their default values. This is done with Student.call()
.
Next we define our new variable. This new variable won't apply to Person, it's specific to Student.
The Student class has a prototype of its own, but we don't want to start from
scratch. We want to reuse any methods defined in Person, so we use Person's
prototype as a starting point. However, we don't want the prototypes to be the
same object, we just want to use the Person one as a baseline that we expand
on. This is why Object.create()
is necessary. It clones the prototype of
Person, making an identical new object so that we're not pointing to the exact
same object. Now we can safely add our new method to our new prototype without
worrying about affecting Person's prototype.
Okay, now we have our Person and Student classes we can use in our application. But what happens if another framework we're using defines its own Person or Student class? Which version will get used? The last one to be loaded will get used and the other ignored, which is obviously not what we want.
Namespaces allow us to separate our classes, functions, variables, whatever we create, off into its own little space that it gets all to itself. We can then reference our own versions with confidence that they won't conflict with other ones.
Since we're in Javascript, where everything is an object, namespaces are also just objects. We can define all of our classes and functions as properties inside this one big object, and they'll be safe from the outside world.
(demonstration of MySchoolLibrary)
Object-oriented programming can be a powerful way to organize your code and reduce the cognitive overhead of working with it. The real world isn't a set of instructions, it's a set of interacting objects. Programs that model the real world are a natural fit for this approach.
Ask for questions after each major or tricky set of new information.