JavaScript Prototypes Explained for Beginners

JavaScript Prototypes Explained for Beginners

·

7 min read

Did you know that everything in JavaScript is an object? You might wonder, "How can everything in JavaScript be an object?" This is possible thanks to prototypes. Don't worry, you'll learn about this in the article. Let's explore JavaScript's most important feature.

What is Prototype?

Prototype is a property of JavaScript which help objects to inherit property or methods from one another.

Let’s take an example,

const person = {
    name: "Viewer",
    greet: function() { console.log("Hello, I am Viewer") }
}

We create an object person and give it a property name and a method greet.

Go to console and try to console log the object person. We get this :

In this, we get the property name and method greet. But we also get a method called [[Prototype]]: Object. What’s this?

This is an object called Prototype. So, the prototype we are discussing is an object. So, if it's an object, it should have its own properties too, right?

Yes, it has many properties (methods) like toString(), valueOf(), etc. Let’s try to console log some of these properties.

console.log(person.toString)

But, we don’t have a method called toString() in the person object. Will it even work? Let’s find out

It’s giving as [object Object]. So, we can call the toString() method even when we don’t define some methods in person object. Why? This is because of prototype chaining.

Prototype Chaining

According to prototype chaining, we can call a method that is not in the object itself, but if it exists in its prototype, it will return a value. If the method is not in the object's prototype, it will check the prototype's prototype. If it finds the method there, it will return the value; otherwise, it will return null or undefined.

Let’s take an example,

const person = {
    greet: function() { console.log("Hello") }
}

In this code, we created an object person and defined a method greet.

We know that we can access some properties of prototype of person which are defined in person object using [[Prototype]] linkage. Let’s understand using diagram;

According to this diagram, we can see that the person's prototype is linked to the Object's prototype through [[Prototype]] linkage. We also notice that there is a constructor we didn't define, and through this constructor, we are connected to the person, and the same applies to the Object.

A constructor is a function that points to its object, and if we log the object to the console, its value will be an object. You can also see this in the image above, which displays all the methods of the prototype.

We can also see that in diagram, person is connected to its methods through Prototype. In backend of JavaScript, when we create anything like object, it connect it’s property through objectName.Prototype.

Shadowing

What happens if you define a property in an object, when a property with the same name is defined in the object's prototype? Let's see:

const person = {
    name: "Viewer",
    toString: () => { console.log("This is toString() method") }
}

In this code, we created an object person and defined property name and a method toString().

Let’s call person’s method toString().

person.toString()
// output : This is toString() method

You might be wondering, "Can we create a method with the same name as the Object's prototype?" The answer is yes, and it will return the value that you define in it.

This happens because JavaScript first checks if the method we are calling exists in the object itself. If it doesn't, JavaScript then looks in the object's prototype. In this case, since the method is in the object, it returns the value we defined.

Prototypal Inheritance

If you've worked with other object-oriented programming languages like C++ or Java, you might already know about inheritance.

In this programming style, we create a class, which acts as a blueprint for an object. If you want to create a new class that includes all the properties of an existing class, you can extend the new class from the existing one. This is known as Classical Inheritance.

But in JavaScript, we use prototypal inheritance instead of classical inheritance.

In prototypal inheritance, object inherit the properties of another object via prototype linkage.

Lets take an example to understand this,

const person = {
    name: "Viewer",
    greet: () => { console.log("Hello") }
}

In this code, we created an object person which have property name and method greet.

By default, the JavaScript engine provides you with a built-in Object() function and an anonymous object that can be referenced by the Object.prototype:

In this circle represents function and square represents object.

The person object has a link to the anonymous object referenced by the Object() function. The [[Prototype]] represents the linkage:

This means that person object can call the methods of Object which is not defined in it. We have already seen it.

We can access the prototype of person object, using __proto__ property. Lets try this :

console.log(person.__proto__)

Note: We should never use __proto__ property in the production code.

The following code shows that person.__proto__ and Object.prototype references to same object.

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

Let’s create a new object teacher which have a method teach().

const teacher = {
    teach: (subject) => {
        console.log(`I teach ${subject} subject`)
    }
}

Like person object, this teacher.__proto__ also points to Object Prototype.

If you want teacher object to get the access of all the property of person object, you can use __proto__ to set the prototype of teacher to person.

teacher.__proto__ = person

Now, teacher object can access property of person via [[Prototype]].

console.log(teacher.name) // Viewer
console.log(teacher.greet()) // Hello

We can call that teacher inherited the property of person. This is called Prototypal Inheritance.

A Standard Way to Implement Prototypal Inheritance in ES5

ES5 provided a standard way to work with prototypal inheritance by using the Object.create() method.

Note that now you should use the newer ES6 class and extends keywords to implement inheritance. It’s much simpler.

The Object.create() method creates a new object and uses an existing object as a prototype of the new object:

Object.create(proto, [propertiesObject])

The Object.create() method accepts two arguments:

  • The first argument (proto) is an object used as the prototype for the new object.

  • The second argument (propertiesObject), if provided, is an optional object that defines additional properties for the new object.

Suppose you have a person object:

let person = {
    name: "Viewer",
    greet: function () {
        return "Hi, I'm " + this.name;
    }
};

The following creates an empty teacher object with the __proto__ of the person object:

let teacher = Object.create(person);

After that, you can define properties for the teacher object:

teacher.name = 'Jane Doe';
teacher.teach = function (subject) {
        return "I can teach " + subject;
}

Or you can do all of these steps in one statement as follows:

let teacher = Object.create(person, {
    name: { value: 'Viewer' } ,
    teach: { value: function(subject) {
        return "I can teach " + subject;
    }}
});

ES5 also introduced the Object.getPrototypeOf() method that returns the prototype of an object. For example:

console.log(Object.getPrototypeOf(teacher) === person);

Output:

true

Defining methods in the JavaScript prototype object

We can define our own methods in the prototype of Object or any other prototype linked to our object. Let's use the earlier example of person and teacher. Here, teacher inherits the properties of person. We want to create a new method in person that returns the full name.

teacher.prototype.fullName = () => this.name

In this case, JS engine add new method fullName() in prototype of teacher which is person.

Why everything in JavaScript is Object?

After all this, you might have figured out why everything is an object. For those of us who aren't sure, let's look at an example.,

const num = [1,2,3]

This is an array num. Let's log num.__proto__ in the console. The output will be

This will print all the methods of the array. But if you look at the bottom, you will see [[Prototype]]: Object. And if you log num.__proto__.__proto__ again, you will see this :

Haven’t we seen it before. That’s right, this is Object.Prototype.

If you take any other data structure, like a string or date, and try to log its __proto__'s __proto__, then you will see that, in the end, it's an object.

Conclusion

In conclusion, understanding JavaScript prototypes is crucial for grasping how inheritance and object properties work in the language. Prototypes allow objects to inherit properties and methods from other objects, enabling efficient code reuse and organization. By leveraging prototype chaining, shadowing, and prototypal inheritance, developers can create complex and dynamic applications. Recognizing that everything in JavaScript is ultimately an object helps in understanding the language's flexibility and power.