22

In ES6, I can create static methods like below. But I need to define a static constructor but no success. I need something that runs only once when the class is loaded. I Is there any way to implement something like this ?

class Commander{

    static onData(){
         console.log("blabla");
    }
}
8
  • You need do some logic when is the first instance of your class created? Commented Feb 2, 2016 at 14:39
  • 1
    You could have a check in your constructor, if a static variable is not set, do your initialisations then set that static variable to true. Commented Feb 2, 2016 at 14:40
  • 3
    @RyuKaplan If you never instantiate the class, this means you are actually only using it as a namespace. You could have an initialize methond in that namespace and simply call it somewhere else in your code. Commented Feb 2, 2016 at 14:41
  • 2
    Wrap the class declaration in a function, initialize things in that function, and call the function. The function finishes by exporting the class. Commented Feb 2, 2016 at 14:44
  • 3
    Like others have said, classes aren't the right tool for the job here Commented Feb 2, 2016 at 14:49

5 Answers 5

22

It does seem neater to have class-setup code inside the class body so the "class expression" is self-contained. ES6 accepts the syntax static constructor() {/* do stuff */} in a class body but never runs it. Perhaps it is for future language expansion? Anyway, here is one way to achieve the desired result. The trick is to initialize a static property with an immediately-executed function expression that does your class setup:

class MyClass {
  static #staticConstructorDummyResult = (function() {
    console.log('static constructor called') // once!
  })()
  constructor () {
    console.log('instance constructor called')
  }
}
let obj = new MyClass(),
    obj2 = new MyClass()

Inside the "static constructor" you can add properties to the class object with MyClass.prop = value, or if you're keen to refer to MyClass as this, change the function expression to an arrow function expression.

The # makes staticConstructorDummyResult private - which should work in all major browsers now (thanks @Donald Duck).

Sign up to request clarification or add additional context in comments.

Comments

12

I need something that runs only once when the class is loaded.

You shouldn't be using classes if you just use them as a bag of methods. Use an object instead. However, it's still possible to run such code. Just put it before or after the class definition.

console.log('before class is created')

class Foo {}

console.log('after class was created');

4 Comments

Just put it before or after the class definition. :D yes
The problem with this is that it doesn't allow you to access private fields.
@DonaldDuck: For accessing private fields you'd need an instance, so I don't know how this would fit into this problem anyway.
@FelixKling Not if the private fields are static.
12

In ES2022 we now have static initialization blocks which look like this:

class Commander {
  static {
    // Arbitrary code goes in here and is run when the class is defined
    // You can use `this` to reference the class (instead of having to use its name):
    this.foo = 'foo'; // sets a static property
  }
}

1 Comment

This is the correct answer, IMO. The only caveat is that browsers and node only started supporting this around September 2021.
5

If you insist on a static constructor: Define a static method and invoke it after the class definition.

class Foo {
  static staticConstructor() {
    console.log('Foo has been constructed statically!');
  }
}

Foo.staticConstructor()

Of course this is not really necessary. Except mabye to clearly express the notion of a static constructor. However this smells like Java.

Felix proposed a fine solution by putting code before or after the class definition.

For example: Do you want to pre-calculate some static members? Just assign the calculation result after the class definition!

class Foo {}

Foo.preCalculated = calculate();

function calculate() {
  console.log('Do some hard work here');
  return 'PRECALCULATED';
}

4 Comments

What about inheritance, is there a way to call super.* in this context?
Good question, why not make it a separate question? Here my take: Each class has its own static constructor. If class A extends class B and they have both static constructors, then class B should be constructed first, then class A. One way to achieve this is putting class B before class A, so that when code for class A is running, class B is ready. You don't need super for static constructors.
Why don't I - I want my static ctor to do the same thing as an instance one, without the need for using custom methods. Using custom methods for objects creation has a missive drawback - you need to make your object consumers aware about that method, which is a leak of dry. I can put a simple example if you're interested.
Then just have code along the class definitions, exactly as Felix Kling proposed, so users don't need to invoke static constructors.
2

With new class property initializers, you may not need a function at all (for simple expressions).

Initializers are = expressions in the class definition context; they act like as being an expression in the constructor, so this is defined (because initializers come after constructors chain).

class Toto {
    foo = 'bar'
    bar = this.foo
    baz = this.method()
    method(){ return 'baz' }
}
console.log( new Toto )
//> Toto {foo: "bar", bar: "bar", baz: "baz"}

Static initializers work the same way, but this is the actual constructor (class), the same way it is defined in a static method.

class Toto {
    static foo = 'bar'
    static bar = this.foo
    static baz = this.method()
    static method(){ return 'baz' }
}
console.dir( Toto )
//> class Toto {name: "Toto", foo: "bar", bar: "bar", baz: "baz", method: ƒ method()}

Using a parent class to declare static methods to be called during initialization is quite handy:

class Base extends HTMLElement {
    static define( tag )
    {
        return customElements.define( this, tag )
    }
}

//then

class MyElement extends Base {
    constructor(){ ... }
    static defined = this.define( 'my-el' )
}

You can also use static getters/setters:

/** utils */
const CSS = css=> { let s = new CSSStyleSheet; s.replaceSync(css); return s }

class Base extends HTMLElement {
    /**
     * node.shadowRoot getter to access shadowRoot with direct creation if not existing yet.
     * @exemple class extends HTMLElement { constructor(){ super(); this.shadowRoot.innerHTML = '...' } }
     * @exemple class extends HTMLElement { html = this.shadowRoot.innerHTML = '...' }
     */
    get shadowRoot()
    {
        return super.shadowRoot || this.attachShadow({mode:'open'})
    }
    adoptedCSS = this.shadowRoot.adoptedStyleSheets = [ this.constructor.css ]
    static set tag( v )
    {
        return customElements.define( this, v )
    }
}

//then

class MyElement extends Base {
    title = 'Default title'
    html = this.shadowRoot.innerHTML = `
        <div class=super>
            <h1>${this.title}</h1>
        </div>
    `
    $title = this.shadowRoot.querySelector('div > h1')

    static css = CSS`
        :host       { outline: 1px solid blue }
        div         { outline: 1px solid green }
    `
    static defined = this.tag = 'my-el'

    // static tag = 'my-el' << this won't work because initializers use  
    // Object.defineProperty and not a direct set, so the setter  
    // will be overwritten!
}

// Note: no need of any framework here

Support:

Sources:

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.