Reputation: 5817
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");
}
}
Upvotes: 22
Views: 36574
Reputation: 3782
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
}
}
Upvotes: 10
Reputation: 278
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:
Upvotes: 2
Reputation: 6695
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).
Upvotes: 22
Reputation: 28717
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';
}
Upvotes: 5
Reputation: 816334
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');
Upvotes: 12