# Prototype
# 1. What is a prototype
Prototypes are the mechanism by which JS objects inherit features from one another.
Prototype is the template for an object. Typically contains lots of methods. For example, array has methods such as push
, length
. These are predefined in the prototype. Each array has a reference to the prototype so the method does not need to be defined every time.
Strings, numbers and Boolean are primitive types. But string also has prototype in JS. You can add your own method to a prototype.
// making a new method to String prototype
String.prototype.yell = ()=>{alert("AHH!!")}
const new_string = "Hi"
new_string.yell()
//AHH!!
2
3
4
5
6
Not something recommended to use, but good to know how prototype works. You could also replace the predefined method with our custom method by using the same name. But this is definitely not recommended.
# .prototype vs proto
String.prototype
actually takes you to the prototype.
__proto__
appears when you're checking inside certain instance of that prototype and is a reference to the prototype.
If anything, you will be using prototype
, __proto__
is seldom used to manipulate anything.
# 2. Factory Functions
function makeColor(r,g,b){
const color = {}
color.r = r
color.g = g
color.b = b
color.rgb = function(){
//destructure this
const {r,g,b} = this
return `rgb(${r}, ${g}, ${b})`;
}
color.hex = function(){
const {r,g,b} = this
return '#' + ((1<<24) + (r << 16) + (g <<8) + b).toString(16).slice(1)
}
return color
}
const firstColor - makeColor(35, 255, 150);
firstColor.hex()
firstColor.rgb()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
This is called a factory function. You can lighten, darken, saturation methods later on as well.
# 3. Constructor Functions
# new
The new
is preferable to factory functions. The downside of the Factory Functions are that there is no reason to have copies of functions such as color.rgb
or color.hex
.
black = makeColor(0,0,0)
white = makeColor(ff, ff, ff)
black.hex == white.hex
// false. This is because hex function is defined per every object.
"hi".slice ==="bye".slice
// true
2
3
4
5
6
7
8
9
On the other hand, the slice
method is not defined per every single string.
The new operator lets developers create an instance of a user-defined object type or built-in object types that has a constructor function.
The new
operator does the following things.
- Creates a blank JS object. (The
const color={}
from factory function) - Links this object to another object
- Passes the new object as the
this
context. (Thecolor.r
from factory function becomesthis.r
) - Returns
this
if the function doesn't return its own object.
// Capitalize first letter to show its a constructor function
function Color(r,g,b){
this.r = r;
this.g = g;
this.b = b;
console.log(this)
}
Color(0, 0, 0)
// this returns window object.
// But this is different when we use the 'new' operator
new Color(0, 0, 0)
// Color {r:0, g:0, b:0}
// r:0
// g:0
// b:0
// __proto__:Object
// constructor: f Color(r,g,b)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Now we can add methods not to the instances, but to the prototype.
function Color(r,g,b){
this.r = r;
this.g = g;
this.b = b;
}
// methods are defined outside the constructor function
Color.prototype.rgb = function(){
//destructure this
const {r,g,b} = this
return `rgb(${r}, ${g}, ${b})`;
}
Color.prototype.hex = function(){
const {r,g,b} = this
return '#' + ((1<<24) + (r << 16) + (g <<8) + b).toString(16).slice(1)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Don't use arrow functions while defining the methods. Because this
behaves differently when we use arrow functions.
# 4. JS Classes
Now a prettier version of what we did
It's class
syntax. This is just a syntactic sugar for exactly what we just did. Cleaner way to make the same thing.
This way you do not have to make the constructor function and separately define functions.
Also, you can't make a mistake by not typing new
as the operator, accidentally renaming the window
.
class Color {
constructor(r,g,b){
console.log("inside constructor")
console.log(r,g,b);
}
}
const white = new Color(255, 255, 255)
// inside constructor
// 255 255 255
2
3
4
5
6
7
8
9
10
The constructor
function is executed immediately when initiated with new
operator.
class Color {
constructor(r,g,b, name){
this.r = r;
this.g = g;
this.b = b;
this.name = name
}
// now we don't have to define function separately using Color.prototype.greet = function(){}
greet(){
return `Hello ${this.name}`
}
hex(){
const {r,g,b} = this
return '#' + ((1<<24) + (r << 16) + (g <<8) + b).toString(16).slice(1)
}
}
const tomato = new Color(255, 67, 89, 'tomato')
tomato.greet()
// Hello tomato
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 5. Extends, Super and Subclasses
Inheritance. (I don't have any)
class Cat {
constructor(name, age){
this.name = name;
this.age = age;
}
eat(){
return `${this.name} is eating`
}
meow(){
return 'meow'
}
}
const monty = new Cat('monty', 9)
monty.eat()
// monty is eating
class Dog {
constructor(name, age){
this.name = name;
this.age = age
}
eat(){
return `${this.name} is eating`
}
bark(){
return 'WOOF'
}
}
const coco = new Dog('coco', 11)
coco.eat()
// coco is eating
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Now let's reactor and remove the duplicates(name, age, eat)
class Pet(){
constructor(name, age){
this.name = name
this.age = age
}
eat(){
return `${this.name} is eating!`
}
}
class Cat extends Pet {
meow(){
return 'meow'
}
}
class Dog extends Pet{
bark(){
return 'woof'
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
You can modify the function by using the same name for function in the subclass. For example, if you make eat()
function for Dog class, this has priority over the Pet's eat
function. You could also make a constructor.
class Pet(){
constructor(name, age){
this.name = name
this.age = age
}
eat(){
return `${this.name} is eating!`
}
}
class Cat extends Pet {
constructor(name, age, livesLeft = 9){
//super lets you use the superclasses constructor
super(name,age)
this.livesLeft = livesLeft;
}
meow(){
return 'meow'
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20