Classes (Javascript)
Classes in Javascript generally are created with two different sections:
- The
prototypes
ormethods
will be how all the instances of the class should be similar (things likeaddYear
for a person class). This is commonly done in theprototype
object. - The constructor will define the properties and how the instances are different (the
name
property of a person class). This is commonly done through the invocation of the constructor, likefunction (name) { this.name = name }
.
Class Types
Decorators
A decorator is a function that accepts an object and adds more properties or functionality to it. It's common to use adjectives as the names of these decorators. Decorators allow for more DRY code and help keep all elements together that should be, allowing little ambiguity in execution.
// this decorator makes obj more 'carlike'
var carlike = function (obj, loc) {
// Properties
obj.loc = loc;
// Methods
obj.move = function () {
obj.loc++;
};
};
var newObj = {};
var newCar = carlike(newObj, 0);
Functional Classes
A functional class is a construct that is capable of making a fleet of objects that conform to the same interface. They are commonly capitalized in name. The functions that produce these functional classes are called constructors. The object that is returned is called an instance of that class.
The difference between a decorator and a functional class is that a decorator accepts their target object as input, whereas the class builds and returns the object it's augmenting.
var Car = function (loc) {
// Properties
var obj = {loc: loc};
// Methods
obj.move = function () {
obj.loc++;
}
return obj;
}
var newCar = Car(0);
Functional Classes with Shared Methods
Using shared methods allows you to save space in memory, as only one instance of a method needs to exist, as opposed to one for every single instance.
// Methods
var carMethods = {
move() {
this.loc++;
}
}
var Car = function (loc) {
// Properties
var obj = { loc: loc };
// Methods
return Object.assign(obj, carMethods);
}
var newCar = Car(0);
Prototypal Classes
Using Object.create()
, we can create an object that inherits all properties of the enclosed object into its prototype
. This will mean that on a failed lookup, it will search within this object for a reference, inheriting the old objects as they were at the time of inheritance.
var Car = function (loc) {
var obj = Object.create(Car.prototype);
// Properties
obj.loc = loc;
return obj;
}
// Methods
Car.prototype.move = function () {
this.loc++;
};
var newCar = Car(0);
Pseudoclassical Classes
The pseudoclassical class is a Javascript syntactic sugar that allows a more conventional style of object oriented programming to be implemented.
By running a function with the new
keyword before it, the interpreter runs the program in a special "construction" mode. Since you will always want to be creating an object and returning it when you are finished, the new
keyword adds these two lines at the beginning and end, respectively, to your function. The object created will be automatically bound to this
and will use the prototype
property found inside that function.
The two different parts of a pseudoclassical class are doing two distinct roles:
- the constructor is defining what is different about each instance. In this example, what
loc
is. - the methods are defining what is similar about each instance. In this example, what
move
does.
// The commented lines in this function are being run "under the hood"
var Car = function (loc) {
// var this = Object.create(Car.prototype);
this.loc = loc;
// copy all prototype methods to `this`
// return this;
};
// Methods
Car.prototype.move = function () {
this.loc++;
};
var newCar = new Car(0);
ES6 Classes (class
)
The ES6 implementation is extremely similar to the pseudoclassical implementation, but uses more syntactic sugar to make it more readable and more similar to other object-oriented languages.
class Car {
constructor(loc) {
// var this = Object.create(Car.prototype);
this.loc = loc;
// copy all prototypes to `this`
// return this;
}
move() {
this.loc++;
}
}
var newCar = new Car(0);
Polymorphism
Polymorphism is the design of objects to be able to share behaviors and override certain shared behaviors to work more specifically on the new object. There is a parent/super class and a child/sub class. The sub class inherits the properties and methods from the super class. In the following examples, the super is Shape
and the sub is Square
or Triangle
.
Functional Classes
var Shape = function (name, sides, sideLength) {
var obj = {
name: name,
sides: sides,
sideLength: sideLength
};
// Abstract method
obj.calcArea = function () {
throw new Error('Cannot calculate area of shape.');
};
obj.calcPerimeter = function () {
return obj.sides * obj.sideLength;
};
return obj;
};
// Equilateral Triangle
var Triangle = function (name, sideLength) {
var obj = Shape(name, 3, sideLength);
obj.calcArea = function () {
return 0.25 * (Math.sqrt(3) * Math.pow(this.sideLength, 2));
};
return obj;
}
var newTriangle = Triangle('triangle', 10);
var Square = function (name, sideLength) {
var obj = Shape(name, 4, sideLength);
obj.calcArea = function () {
return Math.pow(obj.sideLength, 2);
};
return obj;
}
var newSquare = Square('square', 5);
Functional with Shared Methods
var Shape = function (name, sides, sideLength) {
var obj = {
name: name,
sides: sides,
sideLength: sideLength
};
return Object.assign(obj, shapeMethods);
};
var shapeMethods = {
calcArea: function () {
throw new Error('Cannot calculate area of shape.');
},
calcPerimeter: function () {
return this.sides * this.sideLength;
}
};
var Triangle = function (name, sideLength) {
var obj = Shape(name, 3, sideLength);
return Object.assign(obj, triangleMethods);
}
var triangleMethods = {
calcArea: function () {
return 0.25 * (Math.sqrt(3) * Math.pow(this.sideLength, 2));
}
};
var newTriangle = Triangle('triangle', 5);
var Square = function (name, sideLength) {
var obj = Shape(name, 4, sideLength);
return Object.assign(obj, squareMethods);
}
var squareMethods = {
calcArea: function () {
return Math.pow(this.sideLength, 2);
}
};
var newSquare = Square('square', 5);
Prototypal Classes
Using Object.create()
.
var Shape = function (name, sides, sideLength) {
var obj = Object.create(Shape.prototype);
obj.name = name;
obj.sides = sides;
obj.sideLength = sideLength;
return obj;
};
Shape.prototype.calcArea = function () {
throw new Error('Cannot calculate area of shape.');
};
Shape.prototype.calcPerimeter = function () {
return this.sides * this.sideLength;
};
var Triangle = function (name, sideLength) {
var obj = Object.create(Triangle.prototype);
obj.name = name;
obj.sides = 3;
obj.sideLength = sideLength;
return obj;
}
Triangle.prototype = Object.create(Shape.prototype);
Triangle.prototype.constructor = Triangle; // otherwise is `Shape`
Triangle.prototype.calcArea = function () {
return 0.25 * (Math.sqrt(3) * Math.pow(this.sideLength, 2));
};
var newTriangle = Triangle('Triangle', 5);
var Square = function (name, sideLength) {
var obj = Object.create(Square.prototype);
obj.name = name;
obj.sides = 4;
obj.sideLength = sideLength;
return obj;
}
Square.prototype = Object.create(Shape.prototype);
Square.prototype.constructor = Square; // otherwise is `Shape`
Square.prototype.calcArea = function () {
return Math.pow(this.sideLength, 2);
};
var newSquare = Square('square', 5);
Pseudoclassical Classes
Using the new
keyword.
var Shape = function (name, sides, sideLength) {
// this = Object.create(Shape.prototype);
this.name = name;
this.sides = sides;
this.sideLength = sideLength;
// return this;
};
Shape.prototype.calcArea = function () {
throw new Error('Cannot calculate area of shape.');
};
Shape.prototype.calcPerimeter = function () {
return this.sides * this.sideLength;
};
var Triangle = function (name, sideLength) {
this.name = name;
this.sides = 3;
this.sideLength = sideLength;
};
Triangle.prototype = new Shape();
Triangle.prototype.constructor = Triangle; // otherwise is `Shape`
Triangle.prototype.calcArea = function () {
return 0.25 * (Math.sqrt(3) * Math.pow(this.sideLength, 2));
};
var newTriangle = new Triangle('Triangle', 5);
var Square = function (name, sideLength) {
this.name = name;
this.sides = 4;
this.sideLength = sideLength;
};
Square.prototype = new Shape();
Square.prototype.constructor = Square; // otherwise is `Shape`
Square.prototype.calcArea = function () {
return Math.pow(this.sideLength, 2);
};
var newSquare = new Square('square', 5);
ES6 Classes
To create a class that inherits all the properties of another class, use extends
after defining the class
name. You can utilize the parent's constructor by using super()
with the arguments expected in the parent class.
class Shape {
constructor (name, sides, sideLength) {
this.name = name;
this.sides = sides;
this.sideLength = sideLength;
}
calcArea () {
throw new Error('Cannot calculate area of shape.');
}
calcPerimeter () {
return this.sides * this.sideLength;
}
};
class Triangle extends Shape {
constructor (name, sideLength) {
super(name, 3, sideLength);
}
calcArea () {
return 0.25 * (Math.sqrt(3) * Math.pow(this.sideLength, 2));
}
}
var newTriangle = new Triangle('Triangle', 5);
class Square extends Shape {
constructor (name, sideLength) {
super(name, 4, sideLength);
}
calcArea () {
return Math.pow(this.sideLength, 2);
}
}
var newSquare = new Square('square', 5);
Property Lookup/Prototype Chains
If you are looking for a given property of an object, the interpreter will first look at the object itself, and if it fails on that lookup, will look at any other objects that are associated via prototype chain.
To have an ongoing prototype chain, where one object will always default on a failed lookup to searching within another object, you can use var newObj = Object.create(oldObj)
. newObj
will now default to looking up any failed lookups in oldObj
. The values will be calculated during the lookup time, as the values are not stored or copied into the new object. The new object has a link to the old object and will perform on a lookup on the old object in its current state.
References
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
- http://underscorejs.org/#extend
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
- https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Test_your_skills:_Object-oriented_JavaScript
- https://stackoverflow.com/questions/44391149/es6-classes-ability-to-perform-polymorphism
- https://radialglo.github.io/blog/2014/11/24/understanding-pseudoclassical-inheritance-in-javascript/
Incoming Links
Last modified: 202401040446