Understanding JavaScript Prototypes: From Basics to ES6 Classes
Tags: javascript, beginners, programming, tutorial
Published: 2024-05-13 18:58:12

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

```javascript
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 }

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

```javascript
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
```

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.

```javascript
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 }

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

```javascript
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] }

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

```javascript
console.log(luxuryCar.valueOf()) // { doors: 2, seats: 'leather' }
```
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.

```javascript
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
```
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.

```javascript
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.

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

```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 }

```
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! 🚀