ES6 使用静态方法创建新实例为 child class 属性提供未定义

ES6 using static method to create new instance gives undefined for child class attributes

我有以下 child class:

export default class File extends Model {
  selected = false
}

和 parent class:

export default class Model {
  constructor (attributes = {}) {
    if (new.target === Model) {
      throw Error('Not allowed to instantiate Model')
    }

    Object.assign(this, attributes)
  }

  static build (attributes) {
    return new this(attributes)
  }
}

我预计在使用

const file = File.build({selected: true})

file.selected 的结果本来是正确的,但仍然是错误的。在构造函数中记录 "this",我可以看到 File 实例根本没有 selected 属性,返回未定义。

使用当前的 babel 配置

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        targets: {
          node: 'current'
        }
      },
      '@babel/preset-typescript'
    ]
  ],
  plugins: [
    '@babel/plugin-transform-typescript',
    '@babel/plugin-proposal-class-properties'
  ]
}

如果我没有在 child class 上定义 selected,则 file.selected

的结果将为真

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields:

Public instance fields are added with Object.defineProperty either at construction time in the base class (before the constructor body runs), or just after super() returns in a subclass.

this.selected 没有及时设置以便父构造函数看到它(当允许在构造函数中使用 this 时匹配)。一种替代方法是将初始值放在原型上:

File.prototype.selected = false;

你能把 Object.assign 移到 build 方法而不是构造函数吗?

示例:

class Model {
  constructor () {
    if (new.target === Model) {
      throw Error('Not allowed to instantiate Model')
    }
  }

  static build (attributes) {
    const obj = new this(attributes)
    Object.assign(obj, attributes)
    return obj
  }
}

class File extends Model {
  constructor() {
    super()
    this.selected = false
    this.defaultValue = 'default'
  }
}

const file = File.build({selected: true, foo: 'bar'})

console.log(file);