javascriptprototype-chaindomexception

Why does DOMException constructor directly derive from Function.prototype but DOMException.prototype directly from Error.prototype?


In other words, why isn't DOMException like AggregateError, EvalError, etc?

devtools evaluation screenshot Both expressions evaluate to true:

Object.getPrototypeOf(DOMException) === Function.prototype
Object.getPrototypeOf(DOMException.prototype) === Error.prototype

I used to think the following generalization has no counterexamples:

If

For example, the above generalization holds for the following X,Y pairs

  1. HTMLElement, Element
  2. AggregateError, Error
  3. AsyncFunction, Function
  4. Node, EventTarget
  5. Element, Node
  6. RTCError, DOMException

The only case I know of in which the generalization fails is when X = DOMException and Y = Error.

Is there a deeper reason why the DOMException constructor itself cannot have the Error constructor as its [[Prototype]]?


Solution

  • If X.prototype has Y.prototype as its [[Prototype]] object,
    Then X has Y as its [[Prototype]] object

    No, that is not a valid generalisation. It's common, but not ubiquituous. This pattern only started to appear since ES6 classes introduced the inheritance of static properties (including methods in particular) via the prototype chain. Until then, constructor functions were just plain functions, which inherited from Function.prototype. Many libraries still do this.

    However, it's true that this generalisation holds for nearly all builtin classes, both native ones (defined by ECMAScript) and host-provided ones (like the DOM). For the Error hierarchy this was consciously introduced with ES6, in ES5.1 the native error constructors had still inherited from Function.prototype.

    For Web APIs, the Web IDL specification prescribes just this pattern for its interfaces:

    1. Let constructorProto be realm.[[Intrinsics]].[[%Function.prototype%]].
    2. If I inherits from some other interface P, then set constructorProto to the interface object of P in realm.

    and

    1. […] if interface is declared to inherit from another interface, then set proto to the interface prototype object in realm of that inherited interface.
    2. […]
    3. Otherwise, set proto to realm.[[Intrinsics]].[[%Object.prototype%]].

    Looking at those rules, there's actually a second exception: the Window constructor inherits from EventTarget, but the Window.prototype inherits from a WindowProperties object (which in turn inherits from EventTarget.prototype).

    Is there a deeper reason why the DOMException constructor itself cannot have the Error constructor as its [[Prototype]]?

    No. There's a few likely explanations though:

    Edit: Actually Web IDL explicitly describes it as an exception to the rule. It does not inherit from Error, but it still "has its [[Prototype]] internal slot set to the intrinsic object %Error.prototype%". This was discussed in issue #55 and changed in PR #378 to align with the implementation reality - away from the previously described [[Prototype]] value of %Error%.