Course Content
Data types and Values
0/1
Object-oriented programming in JavaScript
0/1
Error handling and debugging in JavaScript
0/1
JavaScript functions for string and array manipulation
0/1
JavaScript Libraries and Frameworks
0/1
JavaScript
About Lesson

Prototypes Tutorial

In JavaScript, prototypes are a fundamental concept that enables objects to inherit properties and methods from other objects. Understanding prototypes is essential for creating efficient and organized code. This tutorial will guide you through the basics of prototypes, constructor functions, and how to work with prototypes effectively.

1. Introduction

Prototypes are a mechanism that allows objects to share properties and methods with other objects. Every JavaScript object has a prototype, which can be thought of as a blueprint for that object. When a property or method is accessed on an object, JavaScript searches the object’s prototype chain to find it.

Weakness of Adding Methods to Objects (Motivation for prototypes)

1.1 Memory Consumption

When you add methods directly to individual objects, each instance of the object will have its own copy of the method. This can consume a significant amount of memory, especially when you have a large number of instances.


    const person1 = {
      name: "Alice",
      sayHello: function() {
        console.log(`Hello from ${this.name}`);
      }
    };

    const person2 = {
      name: "Bob",
      sayHello: function() {
        console.log(`Hello from ${this.name}`);
      }
    };

same problem with doing the following (an object constructor Person):
  function Person(name) {
    this.name = name;
    this.sayHello = function() {
      console.log(`Hello from ${this.name}`);
     };
  }

const person1 = new Person("Alice");
const person2 = new Person("Bob");
console.log(person1)
console.log(person2)
//you can inspect the console to see that each object has it's own method sayHello. 
Instead of just one version shared through the prototype.

1.2. Performance

Having separate copies of methods impacts performance and memory usage.

Function execution and memory allocation are generally slower when compared to shared methods through prototypes.

1.3. Inheritance Issues

Adding methods directly to objects can break expected behavior in inheritance.

If you create new instances based on other instances (inheritance), child instances won’t have access to methods defined on parent instances.

1.4. Dynamic Updates

Modifying a method attached directly to an object doesn’t propagate changes to other instances.

You’d need to update each instance manually, leading to maintenance challenges.

1.5. Difficulty in Code Maintenance

Keeping track of multiple copies of methods across different instances can become difficult to manage, especially in larger projects.

1.6. Prototypal Inheritance

JavaScript’s prototype-based inheritance system is designed to leverage shared methods through prototypes.

By adding methods to the object itself, you’re working against the natural way JavaScript objects are meant to inherit behavior.


    function Person(name) {
      this.name = name;
    }

    Person.prototype.sayHello = function() {
      console.log(`Hello from ${this.name}`);
    };

    //This way the two objects person1 and person2 will share the same method. 
    const person1 = new Person("Alice");
    const person2 = new Person("Bob");

  

Adding methods directly to objects can lead to memory inefficiency, performance issues, and challenges with inheritance and maintenance. Utilizing prototypes and constructor functions with shared methods promotes efficient memory usage, better performance, and proper object-oriented design.

We now proceed to learn the step by step process of working with prototypes

2. Prototype Chain

The prototype chain is a hierarchical structure of linked objects where each object’s prototype points to its parent object’s prototype, forming a chain. If a property or method is not found on an object, JavaScript looks up the prototype chain until it’s found or until the end of the chain is reached (with the ultimate prototype being Object.prototype).

3. Creating Objects with Constructors

Constructor functions are used to create objects with shared properties and methods. They act as templates for creating multiple instances of similar objects.


    // Constructor function for creating Person objects
    function Person(name, age) {
      this.name = name;
      this.age = age;
    }

    const person1 = new Person("Alice", 30);
    const person2 = new Person("Bob", 25);
    console.log(person1); // { name: 'Alice', age: 30 }
  

4. Adding Methods to the Prototype

To avoid duplicating methods for each instance, you can add methods to the prototype of the constructor function. All instances created from the constructor will share the same method.


    // Adding a shared method using the prototype
    Person.prototype.sayHello = function() {
      console.log(`Hello, my name is ${this.name}`);
    };

    person1.sayHello(); // Outputs: Hello, my name is Alice
    person2.sayHello(); // Outputs: Hello, my name is Bob
  

5. Inheritance and Prototype Chain

You can achieve inheritance by setting an object’s prototype to another object. This establishes a prototype chain, allowing the child object to access properties and methods of the parent object.


    // Inheritance using constructor functions and prototypes
    function Student(name, age, grade) {
      Person.call(this, name, age);
      this.grade = grade;
    }

    Student.prototype = Object.create(Person.prototype);
    Student.prototype.constructor = Student;

    const student = new Student("Eve", 18, "A");
    student.sayHello(); // Inherited from Person's prototype
  

6. Using Object.create()

The Object.create() method creates a new object with the specified prototype object. It’s an alternative way to establish prototypes without constructor functions.


    // Creating objects with Object.create()
    const animal = {
      makeSound: function() {
        console.log("Animal sound");
      }
    };

    const cat = Object.create(animal);
    cat.makeSound(); // Outputs: Animal sound

    const lion = Object.create(cat);
    lion.makeSound(); // Outputs: Animal sound
  

State of the art – Class Methods and Prototype (Since ES6)

When you define methods within a class in JavaScript, those methods are added to the prototype of the class. This means that all instances created from that class will share the same set of methods.

  • Note that the methods are defined within the class block, but not within the constructor.
  • Methods defined within a class are automatically added to the class’s prototype.
  • Instances created from the class share the same set of methods through the prototype.
  • Changes to methods in the prototype are reflected in all instances.
  • Method lookup follows the prototype chain, enabling inheritance.

Example


    class Person {
      constructor(name, age) {
        this.name = name;
        this.age = age;
      }

      sayHello() {
        console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
      }

      celebrateBirthday() {
        this.age++;
        console.log(`Happy birthday, ${this.name}! You're now ${this.age} years old.`);
      }
    }

    const person1 = new Person("Alice", 30);
    const person2 = new Person("Bob", 25);

    person1.sayHello(); // Outputs: Hello, my name is Alice and I'm 30 years old.
    person2.sayHello(); // Outputs: Hello, my name is Bob and I'm 25 years old.

    person1.celebrateBirthday(); // Outputs: Happy birthday, Alice! You're now 31 years old.
  

Using the class syntax to define methods in JavaScript automatically adds those methods to the prototype of the class. This promotes method sharing, efficient memory usage, proper inheritance, and easy maintenance.

Powerful Illustration:


    var obj1 = {
        a: 1000
    };
   
   // create a new object called `obj2` and link it to the object `obj1`
   var obj2 = Object.create( obj1 );
   obj2.b = "I love coding";

   obj2.b;     // "I love coding"
   obj2.a;     // 1000 <-- delegated from `obj1` (obj2 object inherited the a variable from obj1 object)
 

This illustrates the object data sharing prototype relationship behind the hood

The a property isn’t actually present on the obj2 object. However, because obj2 is connected through a prototype to obj1, JavaScript automatically searches for a on the obj1 object and finds it there.
This connection might seem like a peculiar aspect of the language. It’s frequently used to imitate or simulate a “class” style of inheritance.
However, a more intuitive approach to using prototypes is through a concept called “behavior delegation.” In this pattern, you deliberately design your linked objects to be capable of delegating certain parts of their behavior from one to another.

 

Task: Experiment to see if obj2 can access a method that is only defined in obj1, similar to variable a.

JavaScript Prototypes Examples

1. Example 1


// Prototype object
var vehiclePrototype = {
  init: function(make, model) {
    this.make = make;
    this.model = model;
  },
  info: function() {
    return this.make + " " + this.model;
  }
};

// Create a new vehicle
var car = Object.create(vehiclePrototype);
car.init("Toyota", "Camry");

// Access properties and methods
console.log(car.info()); // Outputs: Toyota Camry
console.log(car.make);    // Outputs: Toyota

// Create another vehicle
var truck = Object.create(vehiclePrototype);
truck.init("Ford", "F-150");

console.log(truck.info()); // Outputs: Ford F-150
console.log(truck.model);   // Outputs: F-150

  

2. Example 2


    // Creating a prototype chain
    const animal = {
      makeSound: function() {
        console.log("Animal sound");
      }
    };

    const dog = Object.create(animal);
    dog.makeSound(); // Outputs: Animal sound

    const goldenRetriever = Object.create(dog);
    goldenRetriever.makeSound(); // Outputs: Animal sound
  

7. Summary

JavaScript prototypes provide a powerful way to share properties and methods among objects, enabling efficient code organization and inheritance. By understanding and utilizing prototypes, you can build complex applications with maintainable and extensible code structures. Each example demonstrates how prototypes can be employed in different scenarios, showcasing their versatility and importance in JavaScript development.

“` You can copy and paste this code into an HTML file and open it in a web browser to see the tutorial with detailed descriptions and powerful examples under each section.