【问题标题】:Accessors Composition in ES6 ClassesES6 类中的访问器组合
【发布时间】:2016-05-06 00:24:21
【问题描述】:

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

通过组合使用与Douglas Crockford's 对象创建类似的方法,我已经能够从多个类“继承”。

这种方法不适用于访问器(getter/setter)。

我需要使用类,因为这是一项要求。我还发现我正在将功能从一个类复制到另一个类,但我不希望这些从基类继承。

有什么想法吗?

我目前取得的进展在下面的sn-p中:

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

【问题讨论】:

  • 一个东西怎么能既隐藏又开放?
  • 不能。抱歉,这是一个快速演示
  • 你不应该为此使用类。时期。如果您的规范不允许使用 mixins,请将其丢弃。最能帮助您的事情:stackoverflow.com/questions/29879267/…
  • 好像类在 JS 中不适合目的?
  • “mixin”和“interface”只是一个流行词。在 ECMA-262 中没有这样的东西。但这是一种在对话时处理模式的便捷方式。

标签: javascript class ecmascript-6 mixins composition


【解决方案1】:

因为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");

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

【讨论】:

  • 我听说 mixins 正在被淘汰。另外,类需求来自于使用 ESDoc,在项目中改变已经太晚了。
  • 更改为openable.isOpen.bind(openable) 给我openable.isOpen.bind is not a function。我不认为你可以绑定到访问器。
  • @evolutionxbox:Mixins 不会去任何地方。 Re ESDoc,它不能处理像 mixins 这样简单的东西吗? JSDoc 可以。
  • ESDoc 基本上需要类,我看不到它支持 mixins 的地方。我必须为此编写自己的标签。
  • 感谢@T.J.Crowder,你帮了大忙。
【解决方案2】:

除了通过“伪私有财产”表示法和Openable/Hideable 类的原型 getter/setter 的 OP 访问器方法已经值得怀疑之外,traits 将最接近使用类作为同样可疑的要求mixin 代理只是为了满足文档要求。

只要 JavaScript 本身不提供特征,就必须坚持使用 more advanced class based mixin patterns 或记住 Angus Croll's Flight Mixins

必须写入的mixin 的function 主体与classconstructor 主体足够接近。尽管如此,基于函数的 mixin 永远不会被实例化,但总是必须通过 callapply 应用于对象/类型。

一种可能的解决方案,采用这种混合方法,已经可靠地满足了 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 的其他答案,为相关问题提供类似的解决方案,采用相同的方法......

【讨论】:

    猜你喜欢
    • 2016-10-25
    • 1970-01-01
    • 1970-01-01
    • 2016-12-12
    • 2016-03-17
    • 1970-01-01
    • 1970-01-01
    • 2017-11-22
    • 2016-07-04
    相关资源
    最近更新 更多