Classes - JavaScript Core Concepts
Angeline Wang
Posted on October 26, 2022
The Section 2: Functions & Methods of my JavaScript Core Concepts Series is divided into 6 Parts:
Part 1 Functions
Part 2 Classes
Part 3 Methods
Part 4 Callback Methods
Part 5 Scope
Part 6 Hoisting
Classes
Part 1 What are Classes?
= Template for creating Objects
→ Record data using logic that works through the data
How are JavaScript classes created?
= They are created on Prototypes
→ And include some syntax & semantics that are not shared with ES5 class-like semantics
What is the purpose of Classes?
= Class declaration introduced in ES2015
→ Just syntactic sugar: On top of existing prototype-based inheritance
→ Does not add anything new to JavaScript
= Classes can add Properties, Methods & Functions
→ Methods created can be ran later: After the Constructor/Object is created
2 Types of Methods:
1. Instance Methods
= Can be called by Instances
2. Static Methods
= Can be called by the Class
How do you define a Class
?
= 2 Ways to define a Class:
1st Way: Class Expressions
#1 Can be Named or Unnamed
= Name given: Is local to Class’ body
→ But can be accessed through the name
property of the Class
#2 Need to be declared
= Before they can be used
→ Due to hoisting restrictions
Body of a Class
= W/in curly braces
→ Where Class members
ie Methods & Constructor are defined
Execution in Strict Mode
= Stricter syntax for optimal performance
→ Normally silent errors thrown
→ Particular keywords reserved for ECMAScript future versions
2nd Way: Class Declarations
= Using Class
keyword
→ Followed by the name of the Class
Code Example
class Event {
constructor(date, time) {
this.date = date;
this.time = time;
}
}
Function Declarations vs Class Declarations
Function Declarations
= Can be called in code before they are defined
Class Declarations
= Need to be defined before they can be constructed
→ Because the Class is hoisted: But its values are not initialized
Class Re-definition
= Re-defining a Class is not possible
→ An Attempt to redefine a Class will result in: SyntaxError
Alerting you that you are trying to declare a variable that has already been declared
...
Part 2 Constructor Methods
Purpose of Constructors & Prototypes
Act as JavaScript’s main method of defining similar & related Objects
Bring similar functionality facilitated through Classes in other languages
= Does not add extra functionality
→ ThusClass
Declaration made available through ES2015: Just syntactic sugar for existing prototype-based inheritance
Rapidly accumulate many different Objects
= By calling a Constructor different times w/ different arguments
→ Reflective of pattern used in built-in constructors
What are Constructor Methods?
= Here are a few definitions:
Mold for creating multiple Objects w/ same Properties & Methods
Special Method for creating & initializing an Object created w/ a
Class
= Can only be 1 special method w/ nameconstructor
in a ClassAny Function that can be used as a Constructor
= No specific distinction within the language
→ Function can be written to be used as a Constructor, to be called as a Normal Function, or it can be used either way
ie JS functions: Are also Object Constructors
Example
function Event(date, time) {
this.date = date;
this.time = time;
}
Types of Constructors
Type #1: Built-in constructors
= Accessible directly in execution environment at runtime
#1 Object()
#2 Array()
#3 String()
#4 Number()
#5 Boolean()
#6 Date()
#7 Function()
#8 Error()
#9 RegExp()
Creation of Values
= There are 2 ways to create values:
Object Literals
Constructors
Object literals vs Constructors
1 Barely any difference
= Can call Methods on Literals
→ Which means JavaScript will turn the Literal into a temporary Object
→ Helping the Method perform the operation
→ Temporary Object is then trashed when it is no longer useful
2 Why are Object Literals better?
a. Easier to read
b. Faster to run
= Can be optimised at parse time
Simple Objects
Example:
Instead of using this...
var obj = new Object(5);
obj.toFixed(2);
You should use this...
var num = 5;
num.toFixed(2);
Type #2: Custom Constructors
= Define Properties & Methods for your own type of Object
Custom Constructor Creation
= Just like creating a Function
→ Capitalize their name: As convention & for identifiability
= A Constructor can also use the super
keyword
→ To call the Constructor of a superclass
Scope-safe Constructors
What are scope-safe constructors?
= Made to return same result
→ No matter if it’s called with or without new
→ Protects against mistake of omitting new
What is the purpose of scope-safe constructors?
= New programmers can accidentally call a Constructor
→ W/o new
keyword
Resulting in bugs
Which constructors are scope-safe?
= Majority of Built-in Constructors
→ ie Object
, Regex
and Array
= They use a unique pattern to call the Constructor
→ So that in the case new
is not used
= Returns a proper Instance of the Object
→ Through calling Constructor again w/ new
Make a Custom Constructor scope-safe
Code Example
function Event(date, time) {
if (!(this instanceof Event)) {
return new Event(date, time);
}
this.date = date;
this.time = time;
}
var event1 = new Event(“September 24th, 2022”, “12:20PM”);
var event2 = Event(“September 24th, 2022”, “12:20PM”);
= Now both these variable definitions (with and without new
), will return an instance of Event
Object.defineProperty()
Method
= Can be used w/in a Constructor
→ To execute the Property setup needed
Here’s a Use Case…
User Input Manipulation
= Before assignment to an Object
→ Manipulate Value given through date
Parameter into a String
Consisting of Date:
followed by the Value
→ & Assign it to the date
property of Object created
Purpose
= Need to manipulate user-given Values
→ Into a suitable/consistent format
Before saving it to an Object
Code Example
function Event(date) {
Object.defineProperty(this, ‘date’, {
get: function() {
return ‘Event: ‘ + date;
},
set: function(newDate) {
date = newDate;
},
configurable: false
});
}
Accessor Properties
= Do not contain any Properties or Methods
→ Create a Getter to call when Property is read
Expected to return a Value
→ Create a Setter to call when Property is written to
Receives Value assigned at Getter as an Argument
= Entire Constructors: Containing Object.defineProperty()
Method
→ Returns an Instance whose date
property can be set or changed in Object.defineProperty()
Method
→ But cannot be deleted
Above Example
After receiving the value of
date
Getter adds
Date:
to beginning of the ValueReturns that
Object.defineProperty
creates these accessor properties
Purpose of Calling Constructor
= So that JavaScript can perform these 4 Tasks:
Create a new Object
Set
constructor
Property of the Object that is the Variable the Constructor is assigned to
= This Property is different from Standard Properties
→ Because it will not be listed
→ And it cannot be overwrittenUnderlying
constructor
property
= Something that cannot be set manually
→ Can only be set up by JavaScript whennew
keyword is usedObject is created to delete
Event.prototype
in the above exampleJavaScript calls Event()
= In the Context of the new Object
→ Result ofnew Event
: Is the new Object
How to Call a Constructor
Create an Instance of it
= Using thenew
keyword
→ And assigning it to a VariableConstructor assigns given Arguments to Parameters indicated in Initial Definition
Then Constructor assigns given Arguments to the relevant Properties
Code Example
function Event() {
}
var myEvent = new Event();
When to use a Constructor
= Creating various similar Objects
→ With same Properties & Methods
Example Use Case
= Creating an Event
If Function exists
= All need to do is use a new
statement
If the new
Keyword is not used
= Alteration of Global Object will occur
→ Instead of the newly created one
W/o new
= Calling a Function w/o a Return Statement
→ this
in the Constructor points to Window
→ Instead of flat8
Thus, 2 Global Variables are created
W/ new
= Context is switched from Global (Window
)
→ To Instance correctly pointing to flat8
Code Example
var flat8 = new Event(‘September 18th, 2022’)
new
not used & in Strict Mode
= Code would throw an Error
→ B/c Strict Mode designed to protect from accidental calls of a Constructor w/o the new
keyword
Check if Object is Instance
= There are 2 ways to see if an Object is an Instance of another:
1st Way: instanceof
Operator
= Right side of the operator must be a function
→ Even if the prototype.constructor
is reassigned, and the constructor
is lost
→ instanceof
operator will still have the accurate information
Code Example
Check a new Event has been created:
flat-8 instanceof Event
= This will return true
2nd Way: constructor
Property
= constructor
property of any object
→ Will point to the constructor function that created it
→ This property is inherited from its prototype
Code Example
myEvent.constructor === Event;
= Returns true
if myEvent
is an instance of Event
Warning
= Using the constructor
property to check the type of an instance
→ It can be overwritten: So it is bad practice
Constructor vs Function Creation
= When writing a Constructor
→ You should use it like a Constructor
= When writing a Function
→ You should use it like a Function
Call as Function w/o new
Code Example
= Call Event
as a Function w/o using new
Event(‘September 18th, 2022’)
= Returns undefined
→ Creates a date
global variable
Overriding existing date
variable
→ Not what we want
Call Function as Function w/o new
= this
is set to the Global Object
→ Which is the Window
object
→ Which is the Browser
Polluting Namespace
= You should prevent pollution of the namespace
→ This is done by not creating Global Variables
= Confirm if this
is an Event
→ And if it is not, use new
to create an Event
→ And return it
Code Example
function Event(date) {
if(!(this instanceof Person))
return new Event(date)
this.date = date
}
Benefit of Constructor Usage
= To create Methods (instead of creating one big object)
→ Inheritance
Inheritance in JavaScript
= Inheritance in JS is different to inheritance in traditional Object-oriented languages
→ Bc it uses Prototype Inheritance
...
Part 3 Prototype
What is the prototype
?
Here are a few definitions:
A Property
= That all Functions automatically getAn Empty Object
= Receives some special treatment
Object created through Constructor
Creates its own
prototype
ObjectOwn
prototype
Object inherits all Properties of its Constructor’sprototype
= Constructor’sprototype
Object is the Parent of its ownprototype
ObjectAny Properties set on own
prototype
Object
= Can override inherited Properties from Constructor’sprototype
New Property
= Setting a New Property to prototype
Object on the Constructor
→ Will be passed down to any Objects created by Constructor
Code Example
Event.prototype.totalLength = 4;
flat8.totalLength; //This will output 4
Properties created on prototype
= Passed down to Instances
→ But can always be overridden on the Instance itself
Code Example
flat8.totalLength = 10;
flat8.totalLength; //Now this will output 10
Prototype Inheritance
= In place of Classes, JS uses the prototype
→ Allows Inheritance to happen
If Object has many of Properties
= And a Special Parent Property called the prototype
of the Object
→ JS Objects inherit all Properties of its Parent
→ These Properties can be overridden by setting the Property on itself
Define Methods on Objects
Most Memory Efficient
Declare methods on the
prototype
Ensuring only 1
displayEvent
is ever createdAnd it is shared between all the Events created
= Done by makingEvent.prototype
their parent
Code Example
function Event(date){
this.date = date
}
Event.prototype.displayEvent = function(){
return ‘This event is on ‘ + this.date
}
Define Instance Methods
= Defining Methods that can be used from all Instances of a Constructor
→ Done on Constructor’s Prototype Property
Code Example
Event.prototype = {
display: function display() {
return “This is the event!”;
}
}
apply
Method
= Belongs to Function.prototype
→ Can be used to call a Function while binding it to an Object
→ Works even if the Function is not connected to the Object, and if the Object is a different type
Can you bind a Function to an Object yourself?
= Yes
→ Can also include Parameters by passing them as an Array as the 2nd parameter of the apply()
Method
Code Example
function displayEvent(other){
return ‘This event is on ‘ + this.date + ‘and on’ + other.date
}
displayEvent.apply(flat8, [flat9])
apply
Use Cases
1. Calling a Function w/ a List of Arguments
= That is a Dynamic List
ie Math.max(4, 1, 8, 9, 2)
→ Use Math.max
on an arbitrary array (instead of a statically set one)
Use Math.max.apply(Math, myarray)
2. Calling it as a Constructor
= Rather than calling it as a function
→ Create a new
Method of Function.prototype
Code Example
Function.prototype.new = function() {
var args = arguments
var constructor = this
function Fake(){
constructor.apply(this, args)
}
Fake.prototype = constructor.prototype
return new Fake
}
= new
method inside Function.prototype
→ Can call constructors with the new
method
→ Will have feel of calling a constructor
Without actually using a constructor
Example Usage
var nextWeek = [new Event(‘September 24th, 2022’), new Event(‘September 29th, 2022’)]
var args = [‘September 20th, 2022’].concat(nextWeek)
var allEvents = Event.new.apply(Event, args)
= Simplify even further, create a Helper Method
Example Helper Method
Function.prototype.applyNew = function(){
return this.new.apply(this, arguments)
}
var allEvents = Event.applyNew(args)
call
Function
= Another Method of Function.prototype
→ Works like apply()
→ Passes parameters differently
Instead of as an array in the 2nd parameter
They are added to the end
Code Example
displayEvent.call(flat8, flat9)
Subclass Creation: 1st Way
= Set prototype
of subclass to an Instance of superclass
→ ‘subclass’ only called once
→ Cannot customize ‘subclass’ on different calls of ‘superclass’
Code Example
var Event = function Event() {};
Event.prototype = new Venue(“Shoreditch”);
Event.prototype.displayVenue = function displayVenue() { return “Venue Submitted” };
var flat8 = new Event();
flat8 instanceof Event; // Outputs true
flat8 instanceof Venue; // Outputs true
Inheritance w/o Power to Override
= In most prototype-based languages
→ There is inheritance without power to override
→ However, JavaScript does not work like this
Here’s a way to do it:
Code Example
function create(parent) {
var F = function() {};
F.prototype = parent;
return new F();
}
Static Methods & Properties
= static
keyword
→ Defines a static method or property for a class
= Called w/o creating an instance of the class
→ Cannot be called through a class instance
Static methods
= Used to create utility functions for an application
Static Properties
= Used for caches, fixed-configuration
→ Or any other data you do not need to be replicated across instances
Unlike prototype
methods and properties which will appear on individual instances
Code Example
class Event {
constructor(date, time) {
this.date = date;
this.time = time;
}
static displayName = “September Events”;
static timeframe(a, b) {
const tdate = a.date - b.date;
const ttime = a.time - b.time;
return Math.hypot(tdate, ttime);
}
}
const flat8 = new Event(123, 2);
const flat9 = new Event(432, 5);
flat8.displayName; //Outputs undefined
flat9.displayName; //Outputs undefined
flat8.timeframe; //Outputs undefined
flat9.timeframe; //Outputs undefined
console.log(Event.displayName); //Outputs “September Events”;
console.log(Event.timeframe); //Outputs …
Bind this
w/ Prototype & Static Methods
Value for
this
= Must be included when calling a static or prototype method
→ Or else thethis
value will beundefined
inside the methodBody is always executed in strict mode
= So this will occur regardless of whether or not the“use strict”
directive is present
Calling Static & Property Methods
= Static & Property Methods cannot be called on their own
→ Must be called as a Property of another Object
Instance Properties
= Must be defined inside Class Methods
Code Example
class Event {
constructor(date, time) {
this.date = date;
this.time = time;
}
}
Field Declarations
= 2 Different Types of Field Declarations:
1. Public
= Do not need to be made w/ Keyword like let
, const
or var
→ Allow Class Definitions to become more self-documenting
→ Fields will always be there
= Can be declared w/ or w/o a Default Value
Code Example
class Event {
date = “”;
time = “”;
constructor (date, time) {
this.date = date;
this.time = time;
}
}
2. Private
= Cannot be referenced from outside of the Class
→ Can only be read or written w/in Class body
Benefit of Private Field Declarations
Use of Field Declarations not visible outside of Class
= Ensures Classes’ users cannot depend onIntervals (which may change version to version)
Can only be declared upfront in a Field Declaration
= Cannot be created later through assigning them the way normal properties can
Code Example
class Event {
#date = “”;
#time;
constructor(date, time) {
this.#date = date;
this.
}
}
Subclass Creation: 2nd Way
= extends
is used in Class Declarations or Class Expressions
→ For sake of creating a Class as a child of another Class
Constructor in Subclass
= Constructor must first call super()
before using this
Traditional Function-based “Classes”
= Can also be extended
Subclass & Superclass both have same method
= Subclass’ method overrides Superclass’
Code Example
class Event {
constructor(date) {
this.date = date;
}
display() {
console.log(`${this.date} is the date of the event.`);
}
}
class smallEvent extends Event {
constructor(date) {
super(date);
}
speak() {
console.log(`${this.date} is the date of a small event.`);
}
}
const houseWarming = new smallEvent(‘September 22nd, 2022’);
houseWarming.display(); // Output: September 22nd, 2022 is the date of a small event.
Inappropriate Use Case
= Classes cannot extend regular (non-constructible) Objects
→ They must extend another Class
Regular Object Inheritance
= For a Class to inherit Methods & Properties from a Regular Object
→ Instead of another Class
Use Object.setPrototypeOf
Code Example
const Event = {
display() {
console.log(`${this.date} is the date of the event.`);
}
};
class smallEvent = {
constructor(date) {
this.date = date;
}
}
Object.setPrototypeOf(smallEvent.prototype, Event);
const houseWarming = new smallEvent(‘September 22nd, 2022’);
houseWarming.display(); //Output: September 22nd, 2022 is the date of the event.
Species Pattern
Allows creation of Objects which are Instances of Superclass that a Subclass extends
= Instead of creating Objects that are Instances of the SubclassConstructor of Objects created by the Subclass
= Overridden & Set to the SuperclassCan create Instances of Superclass that contain more Methods & Properties
= On top of ones defined in original Class Creation
Code Example
class MyArray extends Array {
static get [Symbol.species] () {
return Array; // This overwrites the Object Constructor and sets it to Array instead of MyArray
}
}
const a = new MyArray(1, 2, 3);
//This allows a to access the default Constructors set at Array
const mapped = a.map((x) => x * x);
//Because a.map() uses a method defined in Array rather than MyArray, the variable called mapped will be an instance of Array rather than MyArray
console.log(mapped instanceof MyArray); //Output: false
console.log(mapped instanceof Array); //Output: true
super
vs Prototype-based Inheritance
Advantage of super
= Ability to call Superclass Methods w/in Subclass
Make Superclass calls w/ super
= Call Methods of Superclass w/in a Method being defined in a Subclass
→ Using super
keyword
Code Example
class Event {
constructor(date) {
this.date = date;
}
display() {
console.log(`${date} is the date of the event.`);
}
}
class smallEvent extends Event {
display() {
super.display();
console.log(`${this.date} is a good time for a small event.`);
}
//Since a method with the same name in the subclass overrides the one defined in the superclass, object created with the subclass will have a method called display() which has additional utility to the objects created with the superclass
}
const houseWarming = new smallEvent(‘September 22nd, 2022’);
houseWarming.display();
//September 22, 2022 is the date of the event.
//September 22, 22 is a good time for a small event.
What are Mixins?
Here are a few definitions:
A Class that includes Methods
= That can be used by other Classes
→ W/o need to be Parent Class of those Classes using its MethodsAbstract Subclasses
= Which are Templates for Classes
Rule for Mixins
= Each Subclass can only have 1 Superclass
→ Multiple inheritance from Tooling Classes is not possible
→ Functionality that needs to be inherited must be provided by 1 Superclass
Using Mix-ins
Create a Function w/ a Superclass as Input
= & a Subclass extending that Superclass as OutputOne Class must accept another Class as an Argument
= This Class must also be accepted as an Argument of another Class
Examples of Mix-ins
const calculatorMixin = (Base) => class extends Base {
calc() {}
};
const randomizerMixin = (Base) => class extends Base {
randomize() {}
};
Example Class using Mix-in
class Foo {}
class Bar extends calculatorMixin(randomizerMixin(Foo)) {}
// Base is in the end set as Base { randomize() {} }
Resources for Further Exploration:
Javascript Constructors and Prototypes By Toby Ho
Understanding JavaScript Constructors By Faraz Kelhini
MDN Web Docs: Object.prototype.constructor
VMWare: JavaScript constructors, prototypes, and the new
keyword
Posted on October 26, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.