在ES6类构造函数

时间:2018-03-13 16:48:57

标签: javascript class ecmascript-6

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

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

我能想到的唯一解决方案是让用户在构建后明确地调用buildIndex - 这似乎是反直觉和不正确的,好像我需要调用"构建"施工结束后。

我错过了什么或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
    }    
}

3 个答案:

答案 0 :(得分:3)

基于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将解析为对象本身。然后工厂函数返回该承诺。在这两种情况下使用工厂函数的优点是,在完全初始化之前,外部世界永远不会使用该对象。

答案 1 :(得分:2)

不要在构造函数中做任何工作(尤其是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构造函数有限吗?

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

答案 2 :(得分:0)

答案似乎是利用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

由于