DEV Community

Jekin Gohel
Jekin Gohel

Posted on

Understanding JavaScript Prototypes: From Basics to ES6 Classes

Description:
JavaScript prototypes are like powerful tools for developers, but they can also be tricky to understand.

In this guide, I'll start by explaining simple stuff like object literals and prototype chains. Then, we'll move on to more advanced things like object constructors and ES6 classes. I'll break down each example step by step, so you'll really get how prototypes work in JavaScript.

By the time you finish this guide, you'll know all about prototypes and be able to use them well in your own projects. That means your code will be neater and easier to keep up.

Introduction:
JavaScript prototypes serve as the foundation of the language's object-oriented programming model. They enable objects to inherit properties and methods from other objects, forming a chain of prototypes.

1. Object Literals and Prototype Chains
Let's start from the beginning. In the tutorial, we talk about object literals, which are just pairs of stuff inside curly braces. These let us make objects quickly and easily. We then explore the idea of prototype chains, where objects inherit properties and methods from their prototypes.

const person = {
    alive: true
}

const musician = {
    plays: true
}

musician.__proto__ = person
console.log(musician.plays) // true
console.log(musician.alive) // true
console.log(musician) // { plays: true }

Enter fullscreen mode Exit fullscreen mode

In this example, we create two objects: person and musician. The musician object inherits properties from person using the __proto__ property. We then demonstrate accessing properties from both objects.

2. Using Object.setPrototypeOf() and Object.getPrototypeOf():
Next, we explore the more modern approach of using Object.setPrototypeOf() and Object.getPrototypeOf() to set and get prototypes, respectively.

Object.setPrototypeOf(musician, person)
console.log(Object.getPrototypeOf(musician)) // { alive: true }
console.log(musician.__proto__) // { alive: true }
console.log(musician.__proto__ === Object.getPrototypeOf(musician)) // true
Enter fullscreen mode Exit fullscreen mode

Here, we achieve the same prototype chain setup as before but using the recommended methods Object.setPrototypeOf() and Object.getPrototypeOf().

3. Extending the Prototype Chain:
Now, let's demonstrate how to extend the prototype chain, moving from a general prototype to more specific ones.

const guitarist = {
    strings: 6,
    __proto__: musician
}

console.log(guitarist.plays) // true
console.log(guitarist.alive) // true
console.log(guitarist.strings) // 6
console.log(guitarist) // { strings: 6 }

Enter fullscreen mode Exit fullscreen mode

In this example, guitarist inherits properties from both musician and person, demonstrating the hierarchical nature of prototype chains.

4. Object with Getter and Setter Methods:
Our tutorial covers creating objects with getter and setter methods, enabling controlled access to object properties.

const car = {
    doors: 2,
    seats: "vinyl",
    get seatMaterial() {
        return this.seats
    },
    set seatMaterial(material) {
        this.seats = material
    }
}

const luxuryCar = {}
Object.setPrototypeOf(luxuryCar, car)
luxuryCar.seatMaterial = "leather"; // Note keyword "this"
console.log(luxuryCar) // { seatMaterial: 'leather' }
console.log(luxuryCar.doors) // 2
console.log(car) // { doors: 2, seats: 'vinyl', [Getter/Setter] }

Enter fullscreen mode Exit fullscreen mode

This example showcases how to define getter and setter methods within an object, providing a cleaner interface for accessing and modifying properties.

5. Walking up the Prototype Chain:
Next, let's discuss how properties and methods are not copied but accessed dynamically from prototypes.

console.log(luxuryCar.valueOf()) // { doors: 2, seats: 'leather' }
Enter fullscreen mode Exit fullscreen mode

Here, we demonstrate accessing the valueOf() method, which is not directly defined on luxuryCar but is accessible via the prototype chain.

6. Object Keys and Looping:
The tutorial covers methods for getting object keys and looping through them, demonstrating the difference between Object.keys() and for...in loops.

console.log(Object.keys(luxuryCar)) // ['doors', 'seats']

Object.keys(luxuryCar).forEach(key => {
    console.log(key)
})
// Output of forEach loop:
// doors
// seats


for (let key in luxuryCar){
    console.log(key)
}

// Output of for...in loop:
// seats
// doors
// seatMaterial
Enter fullscreen mode Exit fullscreen mode

These examples illustrate different approaches to iterating over object keys, highlighting the distinction between own properties and inherited ones.

7. Object Constructors:
Moving on, we delve into object constructors and how they can be used to create objects with shared properties and methods.

function Animal(species){
    this.species = species
    this.eats = true
}

Animal.prototype.walks = function(){
    return `A ${this.species} is walking.` 
}

const bear = new Animal("bear")

console.log(bear.species) // bear
console.log(bear.walks()) // A bear is walking.

Enter fullscreen mode Exit fullscreen mode

Here, we define an Animal constructor function and demonstrate how to create objects using the new keyword, inheriting properties and methods from the constructor's prototype.

8. ES6 Classes for Inheritance:
Finally, the tutorial introduces ES6 classes as a cleaner syntax for inheritance in JavaScript.

class Vehicle {
    constructor(){
        this.wheels = 4
        this.motorized = true
    }
    ready(){
        return "Ready to go!"
    }
}

class Motorcycle extends Vehicle {
    constructor(){
        super()
        this.wheels = 2
    }

    wheelie(){
        return "On one wheel now!"
    }
}

const myBike = new Motorcycle()
console.log(myBike) 
// Motorcycle { wheels: 2, motorized: true }

console.log(myBike.wheels) 
// 2

console.log(myBike.ready()) 
// Ready to go!

console.log(myBike.wheelie()) 
// On one wheel now!

const myTruck = new Vehicle()
console.log(myTruck) 
// Vehicle { wheels: 4, motorized: true }

Enter fullscreen mode Exit fullscreen mode

In this example, we define a Vehicle class with a ready() method and then create a Motorcycle subclass that overrides the wheels property and adds a wheelie() method.

Conclusion:
JavaScript prototypes are a powerful feature of the language, enabling flexible and efficient object-oriented programming. By understanding how prototypes work and how to leverage them effectively, you can write cleaner, more maintainable code. I hope this guide has provided you with the knowledge and confidence to master JavaScript prototypes in your own projects.

Happy coding! 🚀

Top comments (5)

Collapse
 
efpage profile image
Eckehard

The concepts are partly confusing. In C++ you inherit a class from a class, but in JS you inherit objects from objects. Ok, maybe.

But an object constructor is simply a function, nothing else. It is made a constructor by using "new":

function Animal(species){
    this.species = species
    this.eats = true
    return "Hello World"
}
console.log(Animal("bear")) // => Hello World
console.log(new Animal("bear")) // => { species: 'bear', eats: true }
Enter fullscreen mode Exit fullscreen mode

The use of "new" looks like a dirty hack to me. What happens to my World?? Is it gone?

Collapse
 
jekingohel profile image
Jekin Gohel

You're right, constructors in JavaScript are just functions and it inherit objects from objects. They become constructors when called with new.

Collapse
 
devengrdolo profile image
Philip J. Dolo

Great! As a fresher, I appreciate you sharing this basic JavaScript Prototypes. I hope to receive more tutorials on JavaScript Prototypes from you, Sir.

Collapse
 
jekingohel profile image
Jekin Gohel

Thank you for your kind words! I'm glad you found the tutorial helpful.

Collapse
 
krivanek06 profile image
Eduard Krivanek

all in all nice post, however if I ever join a company where I see a code like this (the proto part), I will quit the next day.