JavaScript currently has seven primitives which are: Boolean, Null, Undefined, Number, BigInt, String and Symbol. Everything else is an object— including arrays and dates.
const myArr = [];
typof myArr // returns "object"
const yourArr = new Array;
typof yourArr// also returns "object"
You’re probably used to using the .slice() or .push() methods often on arrays. Where do these build-in methods come from?
const fruits = ['apple', 'peach', 'kiwi'];
fruits.pop(); // removes kiwi
They come from the methods of the prototype of the array object you created.
const apples = new Array(3).fill('apple');
console.log(Array.prototype); // returns methods of Array prototype
When you log the above you will see all the methods that are build-in and ready to use. When you call pop() your are actually calling a function (method) of the prototype object your array is part of. The object that you are calling becomes the value of the this keyword in the method(). So when you do:
apples.pop(); // removes and apple
the apples array is set as the value of the this keyword. The .slice() method is just a shorthand we are using
Array.prototype.pop.call(apples);
With call we can borrow methods to use on objects that contain the this keyword (like Array). So actually, we can borrow the pop() method from the Array prototype to use on array-like objects:
const fishes = {0: 'Neo', 1: 'Clown', 2: 'Angel', length: 3};
Array.prototype.pop.call(fishes);
Object are foundational to almost every aspect of the JavaScript language. The most common way is to create them with curly braces and add properties and methods using dot notation.
const fish = {};
fish.name = 'Nemo';
fish.food = 5;
fish.eat = function(food, amount) {
console.log(`${this.name} is eating ${food}`);
this.food += amount
}
What if we want to create more fishes? We can place the login inside a function and invoke it when we want to create a new fish.
Functional Instantiation
function Fish(name, food) {
const fish = {};
fish.name = name;
fish.food = food;
fish.eat = function(food, amount) {
console.log(`${this.name} is eating ${food}`);
this.food += amount
}
return fish
}
const nemo = Fish('Nemo', 5);
const angel = Fish('Angel', 5);
The eat method is generic, so we are wasting memory by using the above function and making each fish bigger than it needs to be.
Functional Instantiation with shared methods
const fishMethods = {
eat(food, amount) {
console.log(`${this.name} is eating ${food}`);
this.food += amount
}
}
function Fish(name, food) {
const fish = {};
fish.name = name;
fish.food = food;
fish.eat = fishMethods.eat
return fish
}
const nemo = Fish('Nemo', 5);
const angel = Fish('Angel', 5);
We’ve solved the problem of memory waste. It still seems a bit weird using a seperate object with methods in order to share methods across instances. Well, that is where prototypes come into place!
Instead of having a seperate object for methods, we call add the methods directly to the prototype of Fish.
Prototypal Instantiation
function Fish(name, food) {
const fish = {};
fish.name = name;
fish.food = food;
fish.eat = fishMethods.eat
return fish
}
Fish.prototype.eat = function(food, amount) {
console.log(`${this.name} is eating ${food}`);
this.food += amount
}
const nemo = Fish('Nemo', 5);
const angel = Fish('Angel', 5);
Boom! All the functionality is the same, but instead of having to manage a seperate object for all the methods we can just use a built in object from Fish called Fish.prototype.
Object.create
To improve our code even more, we will using Object.create() to avoid failed lookups. This method creates a new object with the specified prototype.
const fish = Object.create(Fish.prototype);
The ‘new’ keyword
When you invoke a function with the new keyword, it automatically assigns the this keyword to the new object that is created. So we do not need to assign the this and return it anymore.
function Fish(name, food) {
// const this = Object.create(Fish.prototype);
fish.name = name;
fish.food = food;
fish.eat = fishMethods.eat
// return this
}
const nemo = new Fish('Nemo', 5);
const angel = new Fish('Angel', 5);
Classes
A class creates a blueprint for an object, basically the same thing as we just made. There is an easier way to do it and JavaScript isn’t a dead language. So, in 2015, EcmaScript 6 introduced support for Classes and the ‘class’ keyword. How are code looks like with a class:
class Fish {
constructor(name, food) {
this.name = name;
this.food = food;
}
eat(food, amount) {
console.log(`${this.name} is eating ${food}`);
this.food += amount
}
}
const nemo = new Fish('Nemo', 5);
const angel = new Fish('Angel', 5);
Looks much better, right?
Why is it still helpful to learn how to do it the old way? Because classes are actually *syntactic sugar *over the old way. The class gets converted to the old way!
Static methods
What if we want a method that we want to use on the class (object constructor) but not in each instance of the class? We just add the static keyword before our method.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
walk(time) {
console.log(`${this.name} is walking for ${time} minutes.`)
}
static sayHi() {
alert('Hi!')
}
}
const tim = new Person('Tim', 25);
tim.walk(5) // works
tim.sayHi() // does not work
Person.sayHi() // works
That’s it, have fun creating objects!
Top comments (0)