在ES6类构造函数中工作

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在ES6类构造函数中工作相关的知识,希望对你有一定的参考价值。

我试图在javascript中构建文档索引,并且无法找到在es6构造函数中正确工作的正确方法。

  • 如果我不调用buildIndex,则该对象不可用,因此它似乎是构造函数的一个很好的候选者
  • 如果我先调用super,它会在没有过滤器的情况下构建索引 - 因此它不是正确的索引,或者我需要丢弃旧的索引
  • 如果我先设置this.filter,它会抛出一个我还没调用过的错误。

我能想到的唯一解决方案是让用户在构造之后明确地调用buildIndex - 这似乎是反直觉和不正确的,好像我需要在构造之后调用“construct”。

我错过了什么或者ES6构造函数有限吗?


class TokenIndex {
    constructor(document, stemmer) {
        this.document = document;
        this.stemmer = stemmer || (x => x);
        this._buildIndex();
    }
    _buildIndex(){
        // do expensive index build
    }
}
class FilteredTokenIndex extends TokenIndex {
    constructor(document, stemmer, filter) {
        this.filterRegex = filter;
        // Fails because super must be called before `this`
        super(document, stemmer); 
    }
    _buildIndex(){
        // do expensive index build
    }    
}

class FilteredTokenIndex2 extends TokenIndex {
    constructor(document, stemmer, filter) {
        // Fails because builds the index without a filter
        super(document, stemmer); 
        this.filterRegex = filter;

    }
    _buildIndex(){
        // do expensive index build
    }    
}
答案

基于ES6的解决方案是不在基础构造函数中放置任何需要完全初始化派生类的内容。相反,将该逻辑放在.init()方法中。

然后,创建一个同时执行new.init()的工厂函数,然后返回一个完全形成的对象。

class TokenIndex {
    constructor(document, stemmer) {
        this.document = document;
        this.stemmer = stemmer || (x => x);
    }
    init() {
        this._buildIndex();
        return this;
    }
    _buildIndex(){
        // do expensive index build
    }
}
class FilteredTokenIndex extends TokenIndex {
    constructor(document, stemmer, filter) {
        super(document, stemmer); 
        this.filterRegex = filter;
    }
    _buildIndex(){
        // do expensive index build
    }    
}

// Factory functions that should be exported and made public
// and should be the only way these instances can be created
// by the outside world
createTokenIndex(document, stemmer) {
    let obj = new TokenIndex(document, stemmer);
    return obj.init();
}

createFilteredTokenIndex(document, stemmer, filter) {
    let obj = new FilteredTokenIndex(document, stemmer, filter);
    return obj.init();
}

这些工厂函数也可以作为类的静态方法,但我更喜欢不导出类,因为这使得外部用户无法使用new实例化它,并可能搞乱对象的初始化。


仅供参考,当您需要在对象的初始化中执行异步操作时,可以使用类似的设计模式。在这种情况下,.init()方法返回一个promise,当所有异步操作完成时,该promise将解析为对象本身。然后工厂函数返回该承诺。在这两种情况下使用工厂函数的优点是外部世界在完全初始化之前永远不会使用该对象。

另一答案

不要在构造函数中做任何工作(尤其是when the work is asynchronous)。构造函数应该只是初始化实例,没有别的。

如果实例在没有完成工作的情况下无法使用,您可以在构造之前以静态方法执行此操作:

class TokenIndex {
    constructor(index, document, stemmer) {
        this.index = index;
        this.document = document;
        this.stemmer = stemmer;
    }
    static buildFrom(document, stemmer = (x => x)) {
        // do expensive index build
        return new this(/* result of work */, document, stemmer);
    }
}
class FilteredTokenIndex extends TokenIndex {
    buildFrom(document, filter, stemmer) {
        // do expensive index build
        return new this(/* result of work */, document, stemmer);
        // or if the filtering is just some preprocessing for the index building,
        return super.buildFrom(filteredDocument, stemmer);
    }
}

我错过了什么或者ES6构造函数有限吗?

不,你没有遗漏任何东西。构造函数不能在任何编程语言中调用可覆盖的方法。

另一答案

答案似乎是利用new.target,以确定这是否是正在构建的实际类,或者父类,并且仅在匹配时执行实际工作。终于在这篇mozilla文章中找到了答案:

https://hacks.mozilla.org/2015/08/es6-in-depth-subclassing/

class TokenIndex {
  constructor(document, stemmer) {
    this.document = document;
    this.stemmer = stemmer || (x => x);
    if (new.target == TokenIndex){
      this._buildIndex();
    }
  }
  _buildIndex(){
    console.log('Build TokenIndex') // do expensive index build
  }
}
class FilteredTokenIndex extends TokenIndex {
  constructor(document, stemmer, filter) {
    super(document, stemmer); 
    this.filterRegex = filter;
    if (new.target == FilteredTokenIndex){
      this._buildIndex();
    }
  }
  _buildIndex(){
    // do expensive index build
    console.log('Build FilteredTokenIndex')
  }    
}


var ti = new TokenIndex(); // prints Build TokenIndex
var fti = new FilteredTokenIndex(); // prints Build FilteredTokenIndex

谢谢

以上是关于在ES6类构造函数中工作的主要内容,如果未能解决你的问题,请参考以下文章

变量 args SFINAE 默认构造函数在 clang 中工作,但在 Visual Studio 2015 中失败

当我从用户获取数据并将其保存到 SQLite 数据库中时,我应该怎么做才能使列表视图在片段中工作

试图让高阶函数在powershell中工作

不能在片段中工作吐司

为啥 addEventListener 在 ES6 中工作一次? [复制]

我如何使函数在派生类中工作?