Classes and objects
Constructor function
- Persist data with the
this
keyword. - No explicit return value.
- Can have parameters.
function SoftwareDeveloper(name) {
this.favoriteLanguage = 'JavaScript';
this.name = name;
}
Creating a new object
- Use the
new
operator.
let developer = new SoftwareDeveloper('David');
- When the
new
operator is not used, no object is created.- The function is invoked like a regular function.
- Since there is no return value, the variable will be assigned to
undefined
.
The instanceof
operator
- This operator confirms whether an object is created by a specific constructor function.
- This operator actually tests whether the constructor appears in the prototype chain of an object.
object instanceof ClassName;
// return a boolean
typeof object
// "object"
this
in objects
- A value for
this
is set when a method is invoked on an object, and that value refers to that object. - Four ways to call functions
- Calling a constructor function with the
new
keyword setsthis
to a newly-created object. - Calling a method sets
this
to the object that owns the method. - Calling a function on its own (i.e. invoking a regular function) sets
this
towindow
. - Calling a function with
call
orapply
allow customization ofthis
.
- Calling a constructor function with the
call()
- Set
this
to custom objects.
// this variable is set to thisObject in the invocation
function.call(thisObject, arguments)
- Invoke functions attached to objects (i.e. methods).
const mockingbird = {
title: 'To Kill a Mockingbird',
describe: function () {
console.log(`${this.title} is a classic novel`);
}
};
const pride = {
title: 'Pride and Prejudice'
};
mockingbird.describe.call(pride);
// 'Pride and Prejudice is a classic novel'
apply()
- Pass in arguments in an array.
- Useful when the number of arguments is unknown.
function.apply(thisObject, [arguments])
bind()
- When methods are passed in as a callback,
this
reference may be lost.
function invokeTwice(cb) {
cb();
cb();
}
const dog = {
age: 5,
growOneYear: function () {
this.age += 1;
}
};
// growOneYear is passed in as a function, so
// this inside growOneYear is set to windows,
// and the age is not updated
invokeTwice(dog.growOneYear);
dog.age;
// 5
- Use anonymous closure to close over the
dog
object.
invokeTwice(function () {
// growOneYear is directly call onto the dog object
dog.growOneYear();
});
dog.age;
// 7
bind()
method provides an less verbose alternative approach.- It is called onto a function and returns a copy of that function with a specified this value.
const growOneYearMethod = dog.growOneYear.bind(dog);
invokeTwice(growOneYearMethod);
dog.age;
// 7
prototype
- Each function has a
prototype
property.prototype
is an object. - An object created by the
new
operator of a constructor function is linked to its constructor'sprototype
. - This link allows the object to access the
prototype
's properties and methods as if it were its own. - Whether accessing a property or invoking a method, the JavaScript interpreter looks for them along the prototype chain in this order:
- The object's own properties.
- The object's constructor's
prototype
. - At the very end of the chain is the
Object()
object, or the top-level parent. - If the property still cannot be found, the property is
undefined
.
- By adding methods to the
prototype
, memory is saved as more objects are instantiated.
function Dalmatian (name) {
this.name = name;
}
// register a method on the prototype
Dalmatian.prototype.bark = function() {
console.log(`${this.name} barks!`);
};
- When new methods are added to
prototype
, existing objects can access the new methods as well. - When
proprotype
is replaced, objects created before the replacement are still linked to the oldprototype
. Objects created after the replacement are linked to the newprototype
.
function Cat() {
this.color = 'white';
}
let cat1 = new Cat();
cat1.color;
// white
cat1.meow();
// exception
Cat.prototype = {
isHungry: false,
meow: function () {
return 'Meow!';
}
};
// new properties and methods are not available
// for existing objects
cat1.isHungry;
// undefined
cat1.meow();
// exception
let cat2 = new Cat();
cat2.color;
// white
cat2.meow();
// Meow!
cat2.isHungry;
// false
Relevant Methods
hasOwnProperty()
hasOwnProperty()
allows user to find the origin of a particular property.- This method takes in a string of the property name, and returns a boolean indicating whether or not the property belongs to the object itself (i.e. that property was not inherited).
isPrototypeOf()
- This method checks whether or not an object exists in another object's
prototype
chain. - It can be used to confirm if a particular object serves as the prototype of another object.
Object.getPrototypeOf()
- This method retrieves the prototype of a given object.
constructor
- A property that returns a reference to the constructor function that creates the object.
- Objects created with literal notation are constructed with the
Object()
constructor function.
_proto_
- An object is secretly linked to its constructor function's
prototype
object through that instance's__proto__
property.
object.__proto__ === object.constructor.prototype;
// true
- It is highly discouraged to reassign the
__proto__
property, or even use it in any written code.- There are compatibility issues across browsers.
- Since the JavaScript engine searches and accesses properties along the prototype chain, mutating an object's prototype can lead to performance issues.
Inheritance
- Inheritance in JavaScript is all about setting up the
prototype
chain.- Users should not use
__proto__
to manage inheritance. - Users should not inherit only the prototype, because this doesn't set up the prototype chain, and any changes made to a child object will also be reflected in a parent object.
- Users should not use
// inappropriate
Child.prototype = Parent.prototype
- Use
Object.create()
to manage inheritance without altering the prototype. - This method takes in a single object as an argument, and returns a new object with its
__proto__
property set to the argument passed into it. - Objects created with this method extend from the passed in argument.
const mammal = {
vertebrate: true,
earBones: 3
};
// rabbit extends mammal
const rabbit = Object.create(mammal);
console.log(rabbit.__proto__ === mammal);
// true
- Create inheritance
function Animal (name) {
this.name = name;
}
Animal.prototype.walk = function () {
console.log(`${this.name} walks!`);
};
function Cat (name) {
// call super constructor; use call
// instead of new operator so that
// no Animal object is created
Animal.call(this, name);
this.lives = 9;
}
Cat.prototype.constructor === Cat
// true
// link Cat.prototype.__proto__ to Animal.prototype
Cat.prototype = Object.create(Animal.prototype);
// now Cat prototype's constructor is set to Animal
// https://stackoverflow.com/a/20830553
Cat.prototype.constructor
// Animal
// so its constructor needs to be changed back
Cat.prototype.constructor = Cat;
// add a method only shared by Cat objects
Cat.prototype.meow = function () {
console.log('Meow!');
};
const bambi = new Cat('Bambi');
bambi.meow();
bambi.walk();
bambi.name;