Search code examples
javascriptinheritancemultiple-inheritance

Why is subclass [class (anonymous)]


Given the example below, could someone please advise why is my C class show as anonymous? I'm trying to validate the instanceOf B.C and it's always false :(

Thank you in advance.

class A {
 constructor() {
   this.a = 1
 }
};

class B extends A {
 constructor() {
   super();
   this.a = 2;
 }
}

B.C = class extends B {
 constructor() {
   super();
   this.a = 3;
 }
}

console.log(A, B, B.C) //[class A], [class B extends A], [class (anonymous) extends B]

let test = new B.C();
console.log(test instanceof B.C) //false
console.log(test.constructor.name) //''

Solution

  • In most situations, assigning an anonymous class expression to something (a variable, a property in an object initializer, the parameter name if using a class expression as the default value of the parameter, ...) assigns the name of the thing to the class. Example:

    const A = class {};
    console.log(A.name);          // "A"
    
    const obj = {
        B: class {},
    };
    console.log(obj.B.name);      // "B"
    
    function example(C = class {}) {
        console.log(C.name);      // "C"
    }
    example();

    But there's a specific exception for when you're adding a property to an existing object. That was deemed a security risk by TC39 (imagine you're storing a class/function you're going to give to untrusted code using a key that should be private to your code and not exposed to theirs), so it's one of the very few places that doesn't happen:

    const obj = {};
    obj.A = class {};
    console.log(obj.A.name); // ""

    If you want B.C to have a name, give it one explicitly:

    B.C = class SomeNameHere extends B {
        // ...
    

    The same is true for anonymous function expressions, see my answer here for more on that.


    In a comment on the answer you've said:

    ...However the instance validation is still showing as false. Any suggestions on what is causing this?...

    That won't happen with the code in the question, regardless of whether B.C has a name. instanceof works just fine either way:

    class A {
        constructor() {
            this.a = 1;
        }
    }
    
    class B extends A {
        constructor() {
            super();
            this.a = 2;
        }
    }
    
    // With a name
    B.C = class BC extends B {
        constructor() {
            super();
            this.a = 3;
        }
    };
    
    // Without a name
    B.D = class extends B {
        constructor() {
            super();
            this.a = 3;
        }
    };
    
    console.log(A, B, B.C); // [class A], [class B extends A], [class (anonymous) extends B]
    
    let test = new B.C();
    console.log(test instanceof B.C);   // true
    console.log(test.constructor.name); // "BC"
    
    let test2 = new B.D();
    console.log(test2 instanceof B.D);   // true
    console.log(test2.constructor.name); // ""
    .as-console-wrapper {
        max-height: 100% !important;
    }