Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheritance from native objects

I seem to miss something about the constructor chain inheritance in Javascript, with native objects. For example :

function ErrorChild(message) { Error.call(this, message); }
ErrorChild.prototype = Object.create(Error.prototype);
var myerror = new ErrorChild("Help!");

Why myerror.message is defined as "" after those statements ? I would expect the Error constructor to define it as "Help!" (and override the default value of Error.prototype.message), like if I was doing :

var myerror = new Error("Help!")

Thanks a lot !

like image 338
ehmicky Avatar asked May 13 '13 08:05

ehmicky


People also ask

How do objects inherit from other objects in JavaScript?

When it comes to inheritance, JavaScript only has one construct: objects. Each object has a private property which holds a link to another object called its prototype. That prototype object has a prototype of its own, and so on until an object is reached with null as its prototype.

How does prototypal inheritance work?

The Prototypal Inheritance is a feature in javascript used to add methods and properties in objects. It is a method by which an object can inherit the properties and methods of another object. Traditionally, in order to get and set the [[Prototype]] of an object, we use Object. getPrototypeOf and Object.

What is object inheritance JS?

In JavaScript, an object can inherit properties of another object. The object from where the properties are inherited is called the prototype. In short, objects can inherit properties from other objects — the prototypes.

How does prototypal inheritance work and how is it different from classical inheritance?

The difference between classical inheritance and prototypal inheritance is that classical inheritance is limited to classes inheriting from other classes while prototypal inheritance supports the cloning of any object using an object linking mechanism.


2 Answers

The simple work-around: Working fiddle

function ErrorChild(name, message) {
    // Error.call(this); this is not needed
    this.name = name;
    this.message = message;
}

ErrorChild.prototype = Object.create(Error.prototype);
ErrorChild.prototype.constructor = ErrorChild;

var myerror = new ErrorChild("test", "Help!");
document.body.innerHTML += myerror.message;

The above doesn't break the expected behaviour. When you throw myerror, the correct name and message will display.

The problem

From the ECMA5 Language Specification:

15.11.1 The Error Constructor Called as a Function

When Error is called as a function rather than as a constructor, it creates and initialises a new Error object. Thus the function call Error(...) is equivalent to the object creation expression new Error(...) with the same arguments.

The problem: Error.call(this), is the equivalent of new Error. But the new Error instantiation will not set the name or message. new Error will initialise message with "" by default.

15.11.4.3 Error.prototype.message # Ⓣ Ⓡ The initial value of Error.prototype.message is the empty String.

Test(proof)

If inside your ErrorChild you were to add:

var test = Error.call(this, message);
console.dir(test);
console.log(test instanceof Error); // true;
console.log(test.message); // "Help!";

The test reflects the ECMA5 spec. An Error instance with a proper message set.

Conclusion:

Because Error.call(arguments); gets automatically translated to new Error(arguments); scope is lost, so the properties are never initialised on the this object.

When Object.create(Error.prototype) is used, the message property takes the default value, the empty String.

like image 109
flavian Avatar answered Oct 01 '22 22:10

flavian


The Problem is not the inheritance chain, but rather the fact that calling Error as a function behaves as calling the constructor, instantiating a new Error Object

Therefore when calling your new ErrorChild the code executed is actually equivalent to

function ErrorChild(message) {
 new Error (message) 
}

Wait. But this only creates a new Error Object which is not used anywhere (and also not returned by your constructor). And simply gets forgotten about.

So this actually doesn't modify your actual ErrorChild instance.

Therefore the message propertie you are accessing is the one shadowed by ErrorChild.prototype which inherits from Error.protyotype and therefore contains the message's default value an Empty String ""

But you can "mimic" an Error Object simply.

As you only need the toString method's defined behaviour and 2 properties

  • name
  • message

To cover the Error's standard properties.

You can simply set your constructors prototype to Error.prototype And assign name and message manually.

function ErrorChild (name,message) {
  this.name = name;
  this.message = message;
}
ErrorChild.prototype = Error.prototype;

Now you can simply create a new ErrorChild (...) instance.

Which you can throw around log and catch ->

throw new ErrorChild("CustomError","Help") //Uncaught CustomError: Help

Heres a JSBin Demo

like image 27
Moritz Roessler Avatar answered Oct 01 '22 21:10

Moritz Roessler