34

Is there any way to use the ECMAScript6 class notation to declare either a static class variable or a default value for an instance variable? Without class what I have in mind would be written as

function MyClass(arg) { if(arg) this.arg = arg; }
MyClass.classVariable = 42;
MyClass.prototype.arg = "no arg specified";

The most obvious ES6-like notation in my opinion would have been

class MyClass {
    constructor(arg) { if(arg) this.arg = arg; }
    static let classVariable = 42;
    let arg = "no arg specified";
}

But this doesn't work, since according to the current spec draft the only productions of ClassElement are static and instance methods and semicolons all by themselves. OK, one can use a pair of getter and setter methods to achieve similar semantics to what I outlined, but I guess at a severe performance penalty and with really bizarre syntax.

Is there some draft which suggests including variables in the class notation, one way or another? If so, what was the suggested syntax, where was it published, where was it discussed, how did the discussion go, and what's the current state of affairs on that front? As it stands, this question can't be answered if no such thing has been discussed before, at any level, but I consider that unlikely.


A bit of background: I'm currently toying with the Google closure compiler performing advanced compilation, using ES6 as input. For that to work, I need a place to put my type annotations for member variables, and I used to place them using syntax like /** @type {string} */ MyClass.prototype.arg; which is a semantic no-op in ECMAScript but provides the type information to the closure compiler nice and easy. I haven't yet found a similarly nice way to do this with a class construct. But if you care to address this aspect, that would be a comment. The question above is about member declarations which are more than no-ops, so that's what an answer here should discuss.

MvG
  • 57,380
  • 22
  • 148
  • 276
  • 2
    I think `class Foo { get foo() { return 123 } }` is as close as it gets – kangax Feb 04 '15 at 19:03
  • I think @kangax's technique is the best bet for anything that will act as an abstract class. I had the same issue; here is a controller I wrote that may/may not give you some ideas. The constructor of the base class does all the heavy lifting for unique functionality (in this case merging arrays), so you just have to ensure that `super()` is always called on sub class constructors: https://github.com/affirmix/tungstenjs/blob/master/src/controller.js. You can see that sub classes don't end up looking too nasty: https://github.com/affirmix/tungstenjs-todomvc/blob/master/src/todoitem-controller.j – Andrew Odri Feb 05 '15 at 08:14

3 Answers3

49

ES6 will almost certainly not cover syntax for defining class variables. Only methods and getters/setters can be defined using the class syntax. This means you'll still have to go the MyClass.classVariable = 42; route for class variables.

If you just want to initialize a class with some defaults, there is a rich new syntax set for function argument and destructuring defaults you can use. To give a simple example:

class Foo {
    constructor(foo = 123) {
        this.foo = foo;
    }
}

new Foo().foo == 123
new Foo(42).foo == 42
lyschoening
  • 18,170
  • 11
  • 44
  • 54
  • 1
    This approach doesn't really work if you're accessing class properties set in the constructor in other functions. See http://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-context-inside-a-callback – jspizziri Nov 01 '15 at 14:57
  • @Jacob in the example I gave I am not setting class properties in the constructor, but rather an instance property with a default. However, if you want to access a class property from within a function on the instance, you can access it using `this.constructor.classProperty`. – lyschoening Nov 02 '15 at 10:03
  • [Class body and method definitions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Class_body_and_method_definitions) has **Prototype methods** section. – tech_me May 14 '17 at 12:10
7

I haven't used Google Closure Compiler, but with Babel you can declare static (scoped to a class) variables as described here. The article is focused on React because of the utility of static members for React, but is applicable to ES6 classes in general.

Syntax is close to your proposed syntax:

class MyClass {
    constructor(arg) { if(arg) this.arg = arg; }
    static defaultArg = 42;
    let arg = MyClass.defaultArg;
}

Note that you'll have to add 'es7.classProperties' to your .babelrc for this to compile. See the Babel 5.0.0 release notes for more info.

I don't know if there's a way to declare a static as a const.

ericsoco
  • 24,913
  • 29
  • 97
  • 127
3

While it's not part of the ES6 spec, this looks like it's coming soon and is already supported by Babel and some others.

Here's the spec: https://github.com/jeffmo/es-class-fields-and-static-properties

And a full list of all the proposals and their status: https://github.com/tc39/ecma262

Clayton Gulick
  • 9,755
  • 2
  • 36
  • 26
  • "this looks like it's coming in ES7" it definitely does not, the feature set is finalised and consists only of the 2 stage-4 proposals. – zerkms Feb 04 '16 at 01:53
  • @zerkms you're absolutely right, I modified my answer, thanks for the correction! I didn't realize es7 had been finalized. – Clayton Gulick Feb 04 '16 at 17:43
  • It does look to be in node 12 so you can play around with it now without Babel or other transpilers. – cwingrav Jun 25 '19 at 14:22