ES6 中的访问器组合 类
Accessors Composition in ES6 Classes
假设我有一个 Thing
class,我想同时成为 Hideable
和 Openable
。
通过组合使用与 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
});
因为 isOpen
和 isHidden
是 访问器 ,你不能只获取它们的副本,你必须在需要时访问它们.
不过,您可以创建自己的 isOpen
、isHidden
,它们使用底层的:
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;
}
});
当然,如果您经常这样做,您会希望有一个辅助函数来设置它,而不是一直重新输入它:
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");
我质疑您 必须 对 Openable
和 Hideable
使用 class
的要求。在我看来,它们更像是 mixins。
此外,OP 的访问器方法通过 "pseudo private property" 符号和原型 getters/setters 用于 Openable
/Hideable
类 已经值得怀疑,trait
s 最接近使用 类 作为 mixin
代理的同样可疑的要求,只是为了满足文档要求。
只要 JavaScript 本身不提供特征,就必须坚持 ore one remembers Angus Croll's Flight Mixins。
必须编写的 mixin 的 function
主体与 class
的 constructor
主体足够接近。尽管如此,基于函数的混合永远不会被实例化,但总是必须通过 call
或 apply
.
应用于 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
假设我有一个 Thing
class,我想同时成为 Hideable
和 Openable
。
通过组合使用与 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
});
因为 isOpen
和 isHidden
是 访问器 ,你不能只获取它们的副本,你必须在需要时访问它们.
不过,您可以创建自己的 isOpen
、isHidden
,它们使用底层的:
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;
}
});
当然,如果您经常这样做,您会希望有一个辅助函数来设置它,而不是一直重新输入它:
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");
我质疑您 必须 对 Openable
和 Hideable
使用 class
的要求。在我看来,它们更像是 mixins。
此外,OP 的访问器方法通过 "pseudo private property" 符号和原型 getters/setters 用于 Openable
/Hideable
类 已经值得怀疑,trait
s 最接近使用 类 作为 mixin
代理的同样可疑的要求,只是为了满足文档要求。
只要 JavaScript 本身不提供特征,就必须坚持
必须编写的 mixin 的 function
主体与 class
的 constructor
主体足够接近。尽管如此,基于函数的混合永远不会被实例化,但总是必须通过 call
或 apply
.
一个可能的解决方案,采用这种混合方法,已经可靠地满足了 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