skip to Main Content

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

  • X and Y are constructor functions, and
  • Y is not the Object() constructor, and
  • X.prototype has Y.prototype as its [[Prototype]] object
    Then
  • X has Y as its [[Prototype]] object

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]]?

2

Answers


  1. 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:

    • it’s too old, DOMException was part of the first browser js implementations already
    • nobody bothered to update it
    • it’s not web-compatible to update it
    • it’s just an oversight
    • it was updated already but browsers didn’t implement it yet

    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%.

    Login or Signup to reply.
  2. Any constructor is inherited from Function because it’s just a function and has nothing to do with its function’s prototype:

    class Test{
    
    }
    
    console.log(Object.getPrototypeOf(Test).constructor.name);
    
    function Test2(){
    
    }
    
    console.log(Object.getPrototypeOf(Test2.constructor).constructor.name);
    
    console.log(Object.getPrototypeOf(Map.constructor).constructor.name);
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search