# 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!!
1
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()
1
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

1
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.

  1. Creates a blank JS object. (The const color={} from factory function)
  2. Links this object to another object
  3. Passes the new object as the this context. (The color.r from factory function becomes this.r)
  4. 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)       
1
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)
}
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
1
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
1
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
1
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'
  }
}

1
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'
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Last Updated: 3/1/2021, 9:19:08 PM