prototipos-en-javascript

Prototypes in JavaScript

  • 6 min

JavaScript is a prototype-based object-oriented language, which means that inheritance and code reuse are managed through a prototype system.

The prototype allows objects to inherit properties and methods from other objects. This inheritance mechanism is called prototypal inheritance.

This allows objects to share methods and properties, which optimizes memory usage.

This is a different approach from other languages that use a class structure, and is a result of JavaScript’s dynamically typed nature (and has its advantages and disadvantages).

Furthermore, it’s one of the hardest concepts to understand in the language, so let’s dive deep into it 👇.

What is a Prototype

The prototype is simply an object that all objects have as an internal property, and to which they can access.

All objects in JavaScript have the [[Prototype]] property, which is a reference to another “parent” object.

This sequence of objects connected by prototypes is called the prototype chain.

When you try to access a property or method that doesn’t exist on the object, JavaScript will search for it in the object’s prototype.

It will continue searching through the prototype chain until it finds the property or until it reaches a null prototype (which generally means it has reached a base object).

Object Creation and Prototypes

Let’s see at which point during object creation the prototype is associated with the object.

Creation with Object Literals

When you create an object using literal notation, JavaScript automatically assigns Object.prototype as the prototype of the new object:

const person = {
  name: "Carlos",
  age: 30
};

console.log(person.__proto__ === Object.prototype); // true
Copied!

Creation with Object.create()

We can also create a new object with a specific prototype using Object.create(proto):

const animal = {
  makeSound() {
    console.log("Sound");
  }
};

const dog = Object.create(animal);
dog.makeSound(); // "Sound"
Copied!

In this case, perro has animal as its prototype, so it inherits the hacerSonido method.

Constructor Function and Prototype

When we use a constructor function with the new operator, a new object is created that inherits from that constructor function’s prototype.

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

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old!`);
};

// Create a new instance of Person
let person1 = new Person("Luis", 30);

// Call the `greet` method from the prototype
person1.greet();  // Output: Hello, my name is Luis and I am 30 years old!
Copied!

In this example:

  • Persona is a constructor function that takes nombre and edad as parameters.
  • Persona.prototype is an object where we can add methods and properties that will be shared by all instances of Persona.
  • The saludar method is added to the prototype of Persona, so all instances of Persona can access it.

The __proto__ Property

Generally, an object’s prototype is publicly available through the __proto__ property.

console.log(luis.__proto__ === Person.prototype); // true
Copied!

Direct access to the __proto__ property is not recommended. Instead of __proto__, it’s preferable to use the static methods provided by the Object object to work with prototypes.

Object.getPrototypeOf(obj)

Gets the prototype of the object.

console.log(Object.getPrototypeOf(luis) === Person.prototype); // true
Copied!

Object.setPrototypeOf(obj, proto)

Sets the prototype of the object.

const newProto = { newProperty: "value" };
Object.setPrototypeOf(luis, newProto);
console.log(luis.newProperty); // "value"
Copied!

Modifying Prototypes

We can add methods to a prototype after instances have been created, which is useful for extending functionalities.

Person.prototype.birthday = function() {
    this.age++;
    console.log(`Congratulations ${this.name}, now you are ${this.age} years old.`);
};

luis.birthday(); // Output: "Congratulations Luis, now you are 31 years old."
Copied!

Prototypal Inheritance in JavaScript

Prototypal inheritance is what allows one object to inherit the properties and methods of another object. This feature is the core of JavaScript’s inheritance system.

For example, consider the following example, where we create two constructor functions: Animal and Perro. The Perro function will inherit from Animal through the prototype:

// Create a base object: Animal
let Animal = {
  greet: function() {
    console.log(`Hello, I am a ${this.type}`);
  }
};

// Create a new object that inherits from Animal
let Dog = Object.create(Animal);

// Define specific properties for Dog
Dog.type = "dog";
Dog.bark = function() {
  console.log("Woof!");
};

// Use the Dog object
Dog.greet(); // "Hello, I am a dog"
Dog.bark();  // "Woof!"
Copied!

In this example:

  • Animal is a base object with a saludar method.
  • Perro is created using Object.create(Animal), which sets Animal as its prototype.
  • Perro has its own properties (tipo = "perro") and additional methods (ladrar).
  • Since Perro inherits from Animal, it can use the saludar method defined in Animal.

Property Shadowing

If a property is defined on an object and also on its prototype, the object’s property “shadows” the prototype’s property.

This means that when accessing the property, the value from the object is obtained, not the one from the prototype.