Prototypes, Classes and the New Operator
What are Prototypes?
To provide inheritance, objects can have a prototype object, which acts as a template object that it inherits methods and properties from.
//String.prototype is a "template object" for every single string.
//We could go crazy and add our own method called yell...
String.prototype.yell = function() {
return `OMG!!! ${this.toUpperCase()}!!!!! AGHGHGHG!`;
};
'bees'.yell(); //"OMG!!! BEES!!!!! AGHGHGHG!"
//We can overwrite an existing Array method like pop (not a good idea):
Array.prototype.pop = function() {
return 'SORRY I WANT THAT ELEMENT, I WILL NEVER POP IT OFF!';
};
const nums = [ 6, 7, 8, 9 ];
nums.pop(); // "SORRY I WANT THAT ELEMENT, I WILL NEVER POP IT OFF!"
Intro to OOP
Factory Functions
Each object has its own color.hex or color.rgb function.
//This functions makes and returns an object every time it is called.
// The resulting objects all follow the same "recipe"
function makeColor(r, g, b) {
const color = {};
color.r = r;
color.g = g;
color.b = b;
color.rgb = function() {
const { r, g, b } = this;
return `rgb(${r}, ${g}, ${b})`;
};
color.hex = function() {
//destruct r,g,b from this
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.hex();
firstColor.rgb(); //"rgb(35, 255, 150)"
const black = makeColor(0, 0, 0);
black.rgb(); //"rgb(0, 0, 0)"
black.hex(); //"#0000s00"
Constructor Functions
Each objects refers to the same Object and uses these methods instead of having there own copy of the method.
// This is a Constructor Function...
// Convention is always Capital letter to begin with!
function Color(r, g, b) {
this.r = r;
this.g = g;
this.b = b;
}
//If you call it on its own like a regular function...
Color(35, 60, 190); //undefined
//It returns undefined. Seems useless!
// *****************
// THE NEW OPERATOR!
// *****************
// 1. Creates a blank, plain JavaScript object;
// 2. Links (sets the constructor of) this object to another object;
// 3. Passes the newly created object from Step 1 as the this context;
// 4. Returns this if the function doesn't return its own object.
Color.prototype.rgb = function() {
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);
};
Color.prototype.rgba = function(a = 1.0) {
const { r, g, b } = this;
return `rgba(${r}, ${g}, ${b}, ${a})`;
};
const color1 = new Color(40, 255, 60);
color1.hex();
const color2 = new Color(0, 0, 0);
color2.hex();
JavaScript Classes
Syntactical “better” way to define Objects, because we don’t need to call “Prototype” everytime we want to add a method to a class.
//Classes are always upper Case per convention!
class Color {
// A constructor will be executed immediatly after a color is created
constructor(r, g, b, name) {
this.r = r;
this.g = g;
this.b = b;
this.name = name;
}
// create same base for rbg and rgba
innerRGB() {
const { r, g, b } = this;
return `${r}, ${g}, ${b}`;
}
rgb() {
return `rgb(${this.innerRGB()})`;
}
rgba(a = 1.0) {
return `rgba(${this.innerRGB()}, ${a})`;
}
hex() {
const { r, g, b } = this;
return (
'#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
);
}
}
const red = new Color(255, 67, 89, 'tomato');
const white = new Color(255, 255, 255, 'white');
Another Example
class Color {
constructor(r, g, b, name) {
this.r = r;
this.g = g;
this.b = b;
this.name = name;
this.calcHSL();
}
innerRGB() {
const { r, g, b } = this;
return `${r}, ${g}, ${b}`;
}
rgb() {
return `rgb(${this.innerRGB()})`;
}
rgba(a = 1.0) {
return `rgba(${this.innerRGB()}, ${a})`;
}
hex() {
const { r, g, b } = this;
return (
'#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
);
}
hsl() {
const { h, s, l } = this;
return `hsl(${h},${s}%, ${l}%)`;
}
fulllySaturated() {
const { h, l } = this;
return `hsl(${h},100%, ${l}%)`;
}
opposite() {
const { h, s, l } = this;
const newHue = (h + 180) % 360;
return `hsl(${newHue},${s}%, ${l}%)`;
}
calcHSL() {
let { r, g, b } = this;
// Make r, g, and b fractions of 1
r /= 255;
g /= 255;
b /= 255;
// Find greatest and smallest channel values
let cmin = Math.min(r, g, b),
cmax = Math.max(r, g, b),
delta = cmax - cmin,
h = 0,
s = 0,
l = 0;
if (delta == 0) h = 0;
else if (cmax == r)
// Red is max
h = ((g - b) / delta) % 6;
else if (cmax == g)
// Green is max
h = (b - r) / delta + 2;
else
// Blue is max
h = (r - g) / delta + 4;
h = Math.round(h * 60);
// Make negative hues positive behind 360°
if (h < 0) h += 360;
// Calculate lightness
l = (cmax + cmin) / 2;
// Calculate saturation
s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
// Multiply l and s by 100
s = +(s * 100).toFixed(1);
l = +(l * 100).toFixed(1);
this.h = h;
this.s = s;
this.l = l;
}
}
const red = new Color(255, 67, 89, 'tomato');
red.hsl();
red.opposite();
red.rgba(0.3);
const white = new Color(255, 255, 255, 'white');
Extends, Super and Subclasses
class Pet {
constructor(name, age) {
console.log('IN PET CONSTRUCTOR!');
this.name = name;
this.age = age;
}
eat() {
return `${this.name} is eating!`;
}
}
// Cat
class Cat extends Pet {
//If we want to add further information
constructor(name, age, livesLeft = 9) {
console.log('IN CAT CONSTRUCTOR!');
//super references the class we are extending from
super(name, age);
this.livesLeft = livesLeft;
}
meow() {
return 'MEOWWWW!!';
}
}
class Dog extends Pet {
bark() {
return 'WOOOF!!';
}
//This overwrites the eat method of pet, because we first check if something exists on the class before we look at the extended class!
eat() {
return `${this.name} scarfs his food!`;
}
}