Difference Between __proto__ and .prototype Properties in JavaScript

Prototypes in JS are pretty confusing. I think it helps to start at a high level. This can be an involved topic but I'll try to keep things succinct.

Understanding __proto__ Property

In JavaScript, every object has something called a "prototype". This is essentially a blueprint for the object, dictating what properties and methods it inherits from another object.

__proto__ is a special property that allows access to an object's prototype. The __proto__ property provides a way to traverse this prototype chain.

For example, consider the toString() method. It's available in almost all objects in JavaScript. However, when you create a new object like let obj = {}, it doesn't directly get its own toString() method. Instead, it inherits it from its prototype.

let obj = {};
console.log(obj.toString()); // [object Object]
console.log(obj.hasOwnProperty("toString")); // false

To truly understand where toString() is defined for an object, we can use __proto__:

let objPrototype = obj.__proto__;
console.log(objPrototype.hasOwnProperty("toString")); // true

Here, we're able to confirm that toString() is indeed defined in the prototype of obj.

But be cautious with __proto__, as it's considered deprecated in favor of the Object.getPrototypeOf() and Object.setPrototypeOf() methods, which provide more reliable and standardized ways to access and modify prototypes:

let obj = {};
let objPrototype = Object.getPrototypeOf(obj);
console.log(objPrototype.hasOwnProperty("toString")); // true

So, it's best to avoid using __proto__ for modern code.

Understanding the prototype Property

The prototype property is specific to constructor functions in JavaScript. It doesn't represent the prototype of the constructors themselves, but rather the prototype of the object instances they create.

function Func() {}
console.log(typeof Func.prototype); // object
let fnObj = new Func();
console.log(Object.getPrototypeOf(fnObj) === Func.prototype); // true

The same holds true for classes:

class Clas {}
console.log(typeof Clas.prototype); // object
let csObj = new Clas();
console.log(Object.getPrototypeOf(csObj) === Clas.prototype); // true

Even built-in types utilize prototype properties for inheritance. For instance, when you create a regular object with {}, it inherits from the Object type, meaning its prototype is the same as Object.prototype.

let obj = {};
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true

However, it's generally not recommended to modify built-in prototype objects, as this could lead to issues later on when new features are introduced.

In modern JavaScript, you typically won't need to use __proto__ or prototype directly. If you need to access an object's prototype, use Object.getPrototypeOf() instead of __proto__.

Get my free, weekly JavaScript tutorials

Want to improve your JavaScript fluency?

Every week, I send a new full-length JavaScript article to thousands of developers. Learn about asynchronous programming, closures, and best practices — as well as general tips for software engineers.

Join today, and level up your JavaScript every Sunday!

Thank you, Taha, for your amazing newsletter. I’m really benefiting from the valuable insights and tips you share.

- Remi Egwuda