ES6 中的访问器组合 类

Accessors Composition in ES6 Classes

假设我有一个 Thing class,我想同时成为 HideableOpenable

通过组合使用与 Douglas Crockford's 对象创建类似的方法,我已经能够 "inherit" 从多个 classes.

此方法不适用于访问器 (getter/setters)。

我需要使用 classes,因为这是一项要求。我还发现我正在将 class 的功能复制到 class,但我不希望这些功能继承自基础 class。

有什么想法吗?

我目前取得的进展在下面的代码片段中:

class Openable {

  constructor(isOpen = false) {
    this._isOpen = isOpen;
  }

  get isOpen() {
    return this._isOpen + ' is stupid.';
  }

  set isOpen(value) {
    this._isOpen = value;
  }

}


class Hideable {

  constructor(isHidden = false) {
    this._isHidden = isHidden;
  }

  get isHidden() {
    return this._isHidden + ' is stupid.';
  }

  set isHidden(value) {
    this._isHidden = value;
  }

}


class Thing {

  constructor(config) {
    let { isOpen, isHidden } = config;

    let openable = new Openable(isOpen);
    this.isOpen = openable.isOpen;

    let hideable = new Hideable(isHidden);
    this.isHidden = openable.isHidden;
  }

}


let thing = new Thing({
  isOpen: true,
  isHidden: false
});

因为 isOpenisHidden 访问器 ,你不能只获取它们的副本,你必须在需要时访问它们.

不过,您可以创建自己的 isOpenisHidden,它们使用底层的:

let openable = new Openable(isOpen);
Object.defineProperty(this, "isOpen", {
    get: () => openable.isOpen,
    set: value => {
        openable.isOpen = value;
    }
});

let hideable = new Hideable(isHidden);
Object.defineProperty(this, "isHidden", {
    get: () => hideable.isHidden,
    set: value => {
        hideable.isHidden = value;
    }
});

Live example on Babel's REPL

当然,如果您经常这样做,您会希望有一个辅助函数来设置它,而不是一直重新输入它:

function wrapProperty(dest, src, name) {
    Object.defineProperty(dest, name, {
        get: () => src[name],
        set: value => { src[name] = value; }
    });
}

(或者通过抓取 属性 描述符并更新它来完成)

然后:

wrapProperty(this, openable, "isOpen");
wrapProperty(this, hideable, "isHidden");

我质疑您 必须 OpenableHideable 使用 class 的要求。在我看来,它们更像是 mixins。

此外,OP 的访问器方法通过 "pseudo private property" 符号和原型 getters/setters 用于 Openable/Hideable 类 已经值得怀疑,traits 最接近使用 类 作为 mixin 代理的同样可疑的要求,只是为了满足文档要求。

只要 JavaScript 本身不提供特征,就必须坚持 ore one remembers Angus Croll's Flight Mixins

必须编写的 mixin 的 function 主体与 classconstructor 主体足够接近。尽管如此,基于函数的混合永远不会被实例化,但总是必须通过 callapply.

应用于 object/type

一个可能的解决方案,采用这种混合方法,已经可靠地满足了 OP 的要求,然后可能看起来像下一个提供的示例代码...

let
  Openable = (function openableMixinFactory () {
    let
      defineProperty = Object.defineProperty,
      isBoolean = (type => (typeof type == 'boolean'));

    return function openableMixinApplicator (isOpen = false) {
      let
        openableCompositeType = this,

        getIsOpen = (() => isOpen),
        setIsOpen = (value => ((isBoolean(value) && (isOpen = value)) || (void 0)));

      defineProperty(openableCompositeType, 'isOpen', {
        get: getIsOpen,
        set: setIsOpen,
        enumerable: true
      });
      return openableCompositeType;
    };
  }()),

  Hideable = (function hideableMixinFactory () {
    let
      defineProperty = Object.defineProperty,
      isBoolean = (type => (typeof type == 'boolean'));

    return function hideableMixinApplicator (isHidden = false) {
      let
        hideableCompositeType = this,

      //getIsHidden = (() => isHidden),
        getIsHidden = (() => [isHidden, 'is stupid.'].join(' ')),
        setIsHidden = (value => ((isBoolean(value) && (isHidden = value)) || (void 0)));

      defineProperty(hideableCompositeType, 'isHidden', {
        get: getIsHidden,
        set: setIsHidden,
        enumerable: true
      });
      return hideableCompositeType
    };
  }());


class Thing {
  constructor(config) {
    let
      {isOpen, isHidden} = config;

    Openable.call(this, isOpen);
    Hideable.call(this, isHidden);
  }
}


var
  thing = new Thing({ isOpen: true/*, isHidden: false*/ });

console.log('thing : ', thing);
console.log('thing.isOpen : ', thing.isOpen);
console.log('thing.isHidden : ', thing.isHidden);
console.log('(thing.isOpen = "xyz") : ', (thing.isOpen = "abc"));
console.log('(thing.isHidden = "xyz") : ', (thing.isHidden = "xyz"));
console.log('thing.isOpen : ', thing.isOpen);
console.log('thing.isHidden : ', thing.isHidden);
console.log('(thing.isOpen = false) : ', (thing.isOpen = false));
console.log('(thing.isHidden = true) : ', (thing.isHidden = true));
console.log('thing.isOpen : ', thing.isOpen);
console.log('thing.isHidden : ', thing.isHidden);
.as-console-wrapper { max-height: 100%!important; top: 0; }

我在 SO 的其他答案,为相关问题提供类似的解决方案,采用相同的方法是...

  • Class inheritance between projects