Classes - JavaScript Core Concepts
Tags: codenewbie, javascript, beginners, tutorial
Published: 2022-10-26 15:01:15

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
1. Act as JavaScript’s main method of defining similar & related Objects 

2. Bring similar functionality facilitated through Classes in _other_ languages 
= Does not _add_ extra functionality
→ Thus `Class` 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:
1. Mold for creating multiple Objects w/ same Properties & Methods

2. Special Method for creating & initializing an Object created w/ a `Class`
= Can only be 1 special method w/ name `constructor` in a Class 

3. Any 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:
1. Object Literals

2. 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 
1. After receiving the value of `date`

2. Getter adds `Date: ` to beginning of the Value 

3. Returns that 

4. `Object.defineProperty` creates these accessor properties

#### Purpose of Calling Constructor 
= So that JavaScript can perform these 4 Tasks:
1. Create a new Object

2. 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 overwritten 

3. Underlying `constructor` property
= Something that cannot be set manually 
→ Can only be set up by JavaScript when `new` keyword is used

4. Object is created to delete `Event.prototype` in the above example

5. JavaScript calls Event()
= In the Context of the new Object
→ Result of `new Event`: Is the new Object 

#### How to Call a Constructor
1. Create an Instance of it
= Using the `new` keyword 
→ And assigning it to a Variable

2. Constructor assigns given Arguments to Parameters indicated in Initial Definition 

3. 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:

1. A Property 
= That all Functions automatically get 

2. An Empty Object
= Receives some special treatment

##### Object created through Constructor 
1. Creates its own `prototype` Object

2. Own `prototype` Object inherits all Properties of its Constructor’s `prototype`
= Constructor’s `prototype` Object is the Parent of its own `prototype` Object

3. Any Properties set on own `prototype` Object 
= Can override inherited Properties from Constructor’s `prototype`

##### 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 
1. Declare methods on the `prototype`

2. Ensuring only 1 `displayEvent` is ever created 

3. And it is shared between all the Events created
= Done by making `Event.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
1. Value for `this` 
= Must be included when calling a static or prototype method
→ Or else the `this` value will be `undefined` inside the method

2. Body 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
1. Use of Field Declarations not visible outside of Class 
= Ensures Classes’ users cannot depend on

2. Intervals (which may change version to version)

3. 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
1. Allows creation of Objects which are Instances of Superclass that a Subclass extends
= Instead of creating Objects that are Instances of the Subclass

2. Constructor of Objects created by the Subclass
= Overridden & Set to the Superclass

3. Can 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:

1. A Class that includes Methods 
= That can be used by _other_ Classes
→ W/o need to be Parent Class of those Classes using its Methods

2. Abstract 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
1. Create a Function w/ a Superclass as Input 
= & a Subclass extending that Superclass as Output 

2. One 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](https://tobyho.com/2010/11/22/javascript-constructors-and/)

[Understanding JavaScript Constructors By Faraz Kelhini](https://css-tricks.com/understanding-javascript-constructors/)

[MDN Web Docs: Classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#static_methods_and_properties)

[MDN Web Docs: Object.prototype.constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor)

[VMWare: JavaScript constructors, prototypes, and the `new` keyword](https://tanzu.vmware.com/content/blog/javascript-constructors-prototypes-and-the-new-keyword)