4

I have been reading a lot lately about some core concepts of JS and inheritance is a bit confusing.

Here are the methods that I know of:

// Base class
function Parent(foo) {
  if (foo === undefined) {
    throw new TypeError('foo is undefined');
  }

  this.foo = foo;
}

// Derived class
function Child(foo, bar) {
  if (foo === undefined) {
    throw new TypeError('foo is undefined');
  }

  if (bar === undefined) {
    throw new TypeError('bar is undefined');
  }

  Parent.call(this, foo);
  this.bar = bar;
}

1. Using a temporary function

function tmpFn(){}
tmpFn.prototype = Parent.prototype;
Child.prototype = new tmpFn();
Child.prototype.constructor = Child;

2. Using a Parent instance

Child.prototype = new Parent();       // fail
Child.prototype = new Parent(null);   // hax
Child.prototype.constructor = Child;

3. Using Object#create

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

After examining the prototype chains for all methods, I realised that they all yield the same result (which is kind of obvious). But in all these methods, there's one thing that's common. We are creating a third object to replace the prototype and then re-set the constructor. are two overrides involved:-

  1. Child.prototype = ...
  2. Child.prototype.constructor = Child

This got me thinking: Why not simply do Child.prototype.__proto__ = Parent.prototype? Or a safer alternative, Object.setPrototypeOf(Child.prototype, Parent.prototype).

What's the harm in this?

3

Mozilla sums up why modifying __proto__ is a bad idea:

Warning: Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation, in every browser and JavaScript engine. The effects on performance of altering inheritance are subtle and far-flung, and are not limited to simply the time spent in obj.__proto__ = ... statement, but may extend to any code that has access to any object whose [[Prototype]] has been altered. If you care about performance you should avoid setting the [[Prototype]] of an object. Instead, create a new object with the desired [[Prototype]] using Object.create().

Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

The Object.setPrototypeOf method comes with a similar warning about performance:

Warning: Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation, in every browser and JavaScript engine. The effects on performance of altering inheritance are subtle and far-flung, and are not limited to simply the time spent in obj.proto = ... statement, but may extend to any code that has access to any object whose [[Prototype]] has been altered. If you care about performance you should avoid setting the [[Prototype]] of an object. Instead, create a new object with the desired [[Prototype]] using Object.create().

Performance issues aside, they continue with warning that __proto__ is a legacy feature:

Warning: While Object.prototype.proto is supported today in most browsers, its existence and exact behavior has only been standardized in the ECMAScript 2015 specification as a legacy feature to ensure compatibility for web browsers.

Source: Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

Furthermore, many browsers allowed you to set the __proto__ property, which allows man in the middle attacks within your class hierarchy (not that this is the only way to do that in JavaScript).

The reason to use Object.create is simple:

The Object.create() method creates a new object with the specified prototype object and properties.

Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

The Object.create method was explicitly created to provide a mechanism for setting up prototypal inheritance, and optionally including definitions for additional properties to avoid the two other workarounds/hacks that you proposed:

  • Using a temporary constructor function pointing to the parent class's prototype

  • Creating a throwaway instance of the parent class, which is not properly initialized, as a means for setting up the prototype chain.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.