68

It seems possible to nest a class in a constructor which can then be instantiated from anywhere within the class, is this official?

[EDIT] E.g.,

class C {

    constructor() {
        class D {
            constructor() { }
        }
    }

    method() {
        var a = new D();  // works fine
    }

}

//var a = new D();  // fails in outer scope

The traceur generated JS https://google.github.io/traceur-compiler/demo/repl.html

$traceurRuntime.ModuleStore.getAnonymousModule(function() {
  "use strict";
  var C = function C() {
    var D = function D() {};
    ($traceurRuntime.createClass)(D, {}, {});
  };
  ($traceurRuntime.createClass)(C, {method: function() {
      var a = new D();
    }}, {});
  return {};
});
//# sourceURL=traceured.js
Machavity
  • 28,730
  • 25
  • 78
  • 91
user5321531
  • 2,545
  • 5
  • 21
  • 27
  • Can you provide a concrete example? – Felix Kling Feb 28 '15 at 17:01
  • Or how did you find this out? No browser implements classes yet. – Felix Kling Feb 28 '15 at 17:12
  • I'm sure I read this somewhere and it duly stayed in mind - hacked with traceur (ref. link above). – user5321531 Feb 28 '15 at 17:18
  • 1
    That is most likely a bug in traceur. – Felix Kling Feb 28 '15 at 17:19
  • [Your example doesn't even work in traceur](https://google.github.io/traceur-compiler/demo/repl.html#class%20C%20%7B%0A%0A%20%20%20%20constructor()%20%7B%0A%20%20%20%20%20%20%20%20class%20D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20constructor()%20%7B%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20method()%20%7B%0A%20%20%20%20%20%20%20%20var%20a%20%3D%20new%20D()%3B%20%2F%2F%20doesn't%20really%20work%0A%20%20%20%20%7D%0A%0A%7D%0A%0Avar%20a%20%3D%20new%20C()%0Aa.method()%20%2F%2F%20fails%20on%20evaluation) – Bergi Feb 28 '15 at 17:20
  • No it is not official in ES6 classes. if you replace `var a = new D()` with `var a = new this.D()` it is just fine though. – Redu Mar 15 '21 at 19:19

3 Answers3

78

No, there are no nested classes in ES6, and there is no such thing as private members in the class syntax anyway if you mean that.

Of course you can put a second class as a static property on another class, like this:

class A {
    …
}
A.B = class {
    …
};

or you use an extra scope:

var C;
{
    class D {
        constructor() { }
    }
    C = class C {
        constructor() { }
        method() {
            var a = new D();  // works fine
        }
    }
}

(There seems to be a bug with traceur as it uses a hoisted var for the class declaration instead of block scope)


With the proposed class field syntax, it will also be possible to write a single expression or declaration:

class A {
    …
    static B = class {
         …
    }
};
Bergi
  • 513,640
  • 108
  • 821
  • 1,164
  • Gain on speed of code execution, lose on load speed and lexical scoping. – user5321531 Feb 28 '15 at 17:22
  • What do you mean by "lose of load speed"? – Bergi Feb 28 '15 at 17:26
  • slower load time for the code, in the case of the first example – user5321531 Feb 28 '15 at 18:03
  • node gives a `ReferenceError: D is not defined` error, I think we can close the books on this one. – user5321531 Feb 28 '15 at 18:33
  • An afterthought, if module B imports class C from module C, and script A imports class B from module B, then that leaves class C out of the scope of script A. Another way of achieving the same thing? – user5321531 Feb 28 '15 at 19:48
  • Well A could still import C. If you want to keep it local, put both classes B and C in module B but export only B. That's basically what my second example with the block-scoped class does, only with blocks instead of modules. – Bergi Feb 28 '15 at 19:58
  • 4
    should be `A.B = class B { ..`. Otherwise the class will be anonymous. – levi Jul 24 '15 at 15:41
  • 1
    @levi: There's nothing wrong with anonymous classes :-) Of course you can add it if you want. – Bergi Jul 25 '15 at 13:34
  • I'm not entirely sure which special flavor of JS I am using right now in NodeJS 6.10, but I have my IDE set as ECMAScript 6, and it supports the "public" and "private" keywords in class definitions. – Dakusan Apr 08 '17 at 20:36
  • 1
    @Dakusan No, that is not a flavour of JS. That's TypeScript (which you maybe have set to compile to ES6). – Bergi Apr 08 '17 at 22:43
  • 1
    Should include the `;` after the `}` for the `B` class – Celso Dantas Apr 17 '18 at 15:29
  • Realize this thread is a bit dated, but it still provides useful guidance. With respect to defining the "second class as a static property on another class", defining B via `A.B = class {...}` does not appear to permit the creation of an instance of class B from within class A (at least on Chrome v76). Eg, from within a class A function, `let n = new this.B()` produces an error of `Uncaught TypeError: this.B is not a constructor`. Defining B via `A.prototype.B = class {...}` resolves the issue... – Trentium Sep 01 '19 at 14:00
  • 2
    @JonTrent You need to refer to `A` itself, not to an instance of it. You can use [either `A.B` or `this.constructor.B`](https://stackoverflow.com/a/28648214/1048572). I would recommend to avoid putting the class on the prototype. – Bergi Sep 01 '19 at 14:19
  • @Bergi, indeed, following my prior comment's example, `let n = new A.B()` now functions, with class B being associated with class A's constructor rather than A's prototype. This answer has certainly de-mystified "nested classes" (if you can call them that), and has been quite instructive... – Trentium Sep 01 '19 at 14:56
  • After creating a new instance of `B`, that is `let instB = new A.B();` from within an `A` instance, Is it possible for `instB` to access properties/methods of the existing `A` instance ? I’m thinking if I can do `let instB = new A.B(this);` !? – akeem Jan 15 '20 at 14:14
  • @akeem No, there's no scoping, and `A.B` doesn't know about any `A` instances. If you need to access a specific instance, you need to pass it as a parameter (e.g. to the constructor), just like you did with `new A.B(this)`. – Bergi Jan 15 '20 at 16:04
5

You could use a getter:

class Huffman {
  constructor() { /* ... */ }
  static get Node() {
    return class Node {
      constructor() {  
        var API = this;
        API.symbol = 0; API.weight = 0;
        return API;    
      }
    };
  }
  get Node() {
    return Huffman.Node;
  }
  encode() { /* ... */ }
  decode() { /* ... */ }
  /* ... */
}

// usage
huffman = new Huffman;
new huffman.Node;
new Huffman.Node;

Which in latest Chrome Dev 44.0.2376.0 on Apple 10.10.2 gives in console

  • new huffman.Node
  • Node {symbol: 0, weight: 0}
  • new Huffman.Node
  • Node {symbol: 0, weight: 0}

In other news, getters are the secret sauce that let's you do a whole bunch of cool things in ES6.

Please Note The above construction breaks instanceof for Node (why? because a whole new class is defined with every get call). To not break instanceof define Node outside of the scope of a single getter, either in the constructor (disabling the Huffman.Node class property and causing instanceof to work within the namespace of a single Huffman instance, and break outside that), or define Node in a sibling or ancestor scope to Huffman (allowing instanceof to work in all scopes below that the one where Node is defined).

user2530580
  • 119
  • 1
  • 5
  • …and with breaking `instanceof`/the prototype chain, you also break performance. You really should just use `huffman.Node = class { constructor(){ this.symbol=0; this.weight=0; } };` – Bergi Apr 28 '15 at 08:20
  • I tend to code one class per module/file nowadays, though have thought that if a class was there to populate an attribute of another class (i.e., being deleted when the instance of the class it is an attribute of is deleted) and was used by no other classes, then I would put the class in the same module - which effectively is the same as nested classes (but not quite, it would not be possible to nest a class in a nested class in this way, a nested scope perhaps at this point). – user5321531 Apr 28 '15 at 13:49
  • Wait, those classes in your code (and the one the OP wants) are not on properties of the *instances*, but they are tied to the whole class itself. – Bergi Apr 28 '15 at 14:21
  • The non-static get is a property of the instance and the static get is a property of the class. Another nice solution is to put related classes into a module, so we could have a Huffman module with Encoder, Node and Decoder classes. They're all module properties and all in the same scope. – user2530580 Apr 28 '15 at 15:32
  • Do you mean my [question on loading scripts](http://stackoverflow.com/questions/8996852/load-and-execute-order-of-scripts)? If no, please comment on that answer there. And no, I don't really have any deeper thoughts on DI. – Bergi Apr 28 '15 at 15:48
4

something like that?

class A {
    constructor () {
        this.B = class {
            echo () {
                console.log('I am B class');
            }
        }
    }
    echo () {
        this.b = new this.B;
        this.b.echo();
    }
}

var a = new A;

a.echo();
Ammatwain
  • 77
  • 3
  • Add more informations to your answer, not only a code block – live2 Apr 04 '18 at 20:31
  • The drawback to this option (relative to Bergi's answer) is that for every instance of class A, there will be a property containing the definition of class B. – Trentium Sep 01 '19 at 14:02
  • This is the only right answer here. The `this.b = new this.B;` part is important. – Redu Mar 15 '21 at 19:15