【问题标题】:Static Constructor in Javascript ES6Javascript ES6 中的静态构造函数
【发布时间】:2016-05-11 10:09:45
【问题描述】:

在 ES6 中,我可以创建如下静态方法。但我需要定义一个静态构造函数但没有成功。我需要在加载类时只运行一次的东西。我有没有办法实现这样的事情?

class Commander{

    static onData(){
         console.log("blabla");
    }
}

【问题讨论】:

  • 你需要做一些逻辑什么时候创建你的类的第一个实例?
  • 您可以检查您的构造函数,如果未设置静态变量,请进行初始化,然后将该静态变量设置为 true。
  • @RyuKaplan 如果您从不实例化该类,这意味着您实际上只是将其用作命名空间。您可以在该命名空间中有一个初始化方法,然后在代码中的其他地方简单地调用它。
  • 将类声明包装在一个函数中,初始化该函数中的内容,然后调用该函数。该函数通过导出类完成。
  • 就像其他人所说的那样,类不是这里工作的正确工具

标签: javascript ecmascript-6


【解决方案1】:

在类主体中包含类设置代码似乎更简洁,因此“类表达式”是自包含的。 ES6 在类体中接受语法static constructor() {/* do stuff */},但从不运行它。也许是为了将来的语言扩展?无论如何,这是实现预期结果的一种方法。诀窍是使用立即执行的函数表达式来初始化静态属性,该函数表达式会执行您的类设置:

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

在“静态构造函数”中,您可以使用MyClass.prop = value 向类对象添加属性,或者如果您希望将MyClass 称为this,请将函数表达式更改为arrow function expression

您可以使用Object.defineProperty() 使_staticConstructorDummyResult 不可枚举,或者如果您不介意需要Chrome(目前在Firefox 中无法使用),您可以在名称前面添加#使其成为私有财产。

【讨论】:

    【解决方案2】:

    我需要在加载类时只运行一次的东西。

    如果您只是将它们用作一组方法,那么您不应该使用类。改用一个对象。但是,仍然可以运行此类代码。只需将它放在类定义之前或之后。

    console.log('before class is created')
    
    class Foo {}
    
    console.log('after class was created');
    

    【讨论】:

      【解决方案3】:

      如果你坚持使用静态构造函数:定义一个静态方法并在类定义之后调用它。

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

      当然这不是必须的。除了可以清楚地表达静态构造函数的概念。然而,这闻起来像 Java。

      Felix 提出了一个很好的解决方案,即在类定义之前或之后放置代码。

      例如:你想预先计算一些静态成员吗​​?只需在类定义后赋值计算结果即可!

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

      【讨论】:

      • 继承呢,有没有办法在这种情况下调用super.*?
      • 好问题,为什么不单独提出一个问题呢?这是我的看法:每个类都有自己的静态构造函数。如果 A 类扩展了 B 类并且它们都有静态构造函数,则应先构造 B 类,然后再构造 A 类。实现此目的的一种方法是将 B 类放在 A 类之前,这样当 A 类的代码运行时,B 类准备好了。静态构造函数不需要super
      • 我为什么不这样做 - 我希望我的静态 ctor 做与实例相同的事情,而不需要使用自定义方法。使用自定义方法创建对象有一个严重的缺点——你需要让你的对象消费者知道该方法,这是干的泄漏。如果你有兴趣,我可以举一个简单的例子。
      • 然后只需在类定义中添加代码,就像 Felix Kling 建议的那样,因此用户不需要调用静态构造函数。
      【解决方案4】:

      使用新的类属性初始化器,您可能根本不需要函数(对于简单的表达式)。

      初始化器是类定义上下文中的=表达式,它就像是构造器中的表达式,所以定义了this(因为初始化器在构造器链之后)。

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

      静态初始化器的工作方式相同,但this 是实际的构造函数(类),与在静态方法中定义的方式相同。

      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()}
      

      使用父类声明初始化时调用的静态方法非常方便:

      class Base extends HTMLElement {
          static define( tag )
          {
              return customElements.define( this, tag )
          }
      }
      
      //then
      
      class MyElement extends Base {
          constructor(){ ... }
          static defined = this.define( 'my-el' )
      }
      

      您也可以使用静态 getter/setter:

      /** 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
      

      支持:

      来源:

      【讨论】:

        猜你喜欢
        • 2017-04-07
        • 2016-08-28
        • 2018-09-25
        • 2012-02-21
        • 2011-05-06
        • 2020-05-05
        • 2023-03-29
        • 2011-03-01
        • 1970-01-01
        相关资源
        最近更新 更多