Object Oriented Programming (OOP)
- a programming model based around the idea of objects
- the objects are constructed from what are called classes, which are like the blueprints
of the objects --- objects created from classes are called instances
- strive to make classes abstract and modular so they can be shared through all parts of an
application
- JavaScript does not have classes built into it, but we can mimic the behaviour with things it
does have --- functions and objects
Constructor Functions
new
keyword
- WRONG example:
function House (bedrooms, bathrooms, numSqft) {
this.bedrooms = bedrooms;
this.bathrooms = bathrooms;
this.numSqfy = numSqft;
}
let firstHouse = House(2, 2, 1000); // trying to create a House object
firstHouse; // "undefined" --- so it obviously didn't work
firstHouse.bedrooms; // "Uncaught TypeError: Cannot read property 'bedrooms'
// of undefined"
- we are not returning anything from the function so our House function returns undefined
- we are not explicitly binding
this
or placing it inside a declared object --- this means
the value of this
is the global object
- GOOD example:
function House (bedrooms, bathrooms, numSqft) {
this.bedrooms = bedrooms;
this.bathrooms = bathrooms;
this.numSqfy = numSqft;
}
let firstHouse = new House(2, 2, 1000); // actually created an object this time!
firstHouse.bedrooms; // 2
firstHouse.bathrooms; // 2
firstHouse.numSqft; // 1000
- example with function inside the constructor:
function Dog (name, age) {
this.name = name;
this.age = age;
this.bark = function () {
console.log(this.name + ' just barked!');
}
}
let rusty = new Dog('Rusty', 3);
let fido = new Dog('Fido', 1);
rusty.bark(); // "Rusty just barked!"
fido.bark(); // "Fido just barked!"
What is new
Doing?
- first it creates an empty object
- then it sets
this
to be that empty object
- it adds the line
return this
to the end of the function which follows it- ==>
new
must be used with a function or we will get a TypeError
- finally, it adds a property onto the empty object called 'proto__' (also called 'dunder
proto' because of the double underscores), which links the prototype property on the
constructor function to the empty object (for more on this see the Prototypes section)
Multiple Constructors
function Car (make, model, year) {
this.make = make;
this.model = model;
this.year = year;
// we can also set properties on the keyword `this` that are preset values
this.numWheels = 4;
}
function Motorcycle (make, model, year) {
this.make = make;
this.model = model;
this.year = year;
this.numWheels = 2;
}
- this has a lot of duplication --- so we want to refactor our code quite a bit using call and apply
// Car stays the same
function Car (make, model, year) {
this.make = make;
this.model = model;
this.year = year;
this.numWheels = 4;
}
// Motorcycle refactored using call
function Motorcycle (make, model, year) {
Car.call(this, make, model, year); // the `this` argument is telling the function to make
this.numWheels = 2; // the Motorcycle the object refered to instead of Car
}
// Motorcycle refactored using apply
function Motorcycle (make, model, year) {
Car.apply(this, [make, model, year]);
this.numWheels = 2;
}
// Motorcycle refactored again using both apply and the arguments keyword --- that makes this
// version the nicest
function Motorcycle (make, model, year) {
Car.apply(this, arguments);
this.numWheels = 2;
}
Prototypes
- a circle is a function and a square is an object
- every constructor function has a property on it called 'prototype', which is an object
- the prototype object has a property on it called 'constructor', which points back to the
constructor function
- anytime an object is created using the
new
keyword, a property called 'proto__' gets
created, linking the object and the prototype property of the constructor function- the prototype is shared among all objects created by that constructor function
// constructor function
function Person (name) {
this.name = name;
}
// it hs a prototype property
Person.prototype; // ▼ {constructor: f}
// ▶ constructor: f Person(name)
// ▶︎ __proto__: Object ︎
// objects created from the Person constructor
let elie = new Person('Elie');
let colt = new Person('Colt');
// since we used `new`, we have established a link between the object and the prototype property
// that we can access using __proto__
elie.__proto__ === Person.prototype; // true
colt.__proto__ === Person.prototype; // true
// the Person.prototype object also has a property called constructor which points back to the
// function
Person.prototype.constructor === Person; // true
// constructor function
function Person (name) {
this.name = name;
}
// objects created from the Person constructor
let elie = new Person('Elie');
let colt = new Person('Colt');
Person.prototype.isInstructor = true; // this is adding the isInstructor as a
// property on the prototype (note: it will always be
// true for any Person object)
elie.isInstructor; // true
colt.isInstructor; // true
- we were able to access isInstructor using the proto__ link --- linked objects can access any
properties on the prototype
- example:
- you can create new arrays with
let arr = [];
--- this is shorthand for new Array
--- an
array only has one property, length, but arrays have many accessible methods available
through proto__
* you can check this in the console with arr.__proto__ === Array.prototype // true
Prototype Chain
- JS finds methods and properties by looking at the object --- if it can't find what you're
looking for, it will check the object's proto__ --- if still not found it will check the
Object prototype though it's proto__ --- if still not found it will be undefined
- this is the Prototype Chain
- every object has access to the Object prototype
- the Object prototype is linked to
null
--- this is where the prototype chain ends
- in JS every object has a method called
hasOwnPropery
which returns true if the object has a
property specified as a parameter of the hasOwnProperty
method- example:
arr.hasOwnProperty('length'); // true
- the
hasOwnProperty
method can be found in the Object proto__ within the Array
proto__
function Person (name) {
this.name = name;
this.sayHi = function () {
return 'Hi ' + this.name;
}
}
elie = new Person('Elie');
elie.sayHi(); // "Hi Elie"
// this code works but it inefficient
// every time we make an object using the new keyword we have to redefine the `sayHi` function
// , but it is the same for everyone
// here is a refactored version with `sayHi` on the prototype instead
function Person (name) {
this.name = name;
}
Person.protoype.sayHi = function () {
return "Hi " + this.name;
}
elie = new Person('Elie');
elie.sayHi(); // "Hi Elie"
// place all properties do not want shared inside the constructor function
function Vehicle (make, model, year) {
this.make = make;
this.model = model;
this.year = year;
this.isRunning = false;
}
// all the shared functions are in the Vehicle.prototype
Vehicle.prototype.turnOn = function () {
this.isRunning = true;
}
Vehicle.prototype.turnOff = function () {
this.isRunning = false;
}
Vehicle.prototype.honk = function () {
if (this.isRunning) {
return 'beep!';
}
}