MDN 上有一篇关于使用和创建 mix-ins 的文章 (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends#mix-ins)。我尝试在 TypeScript 中实现这个:
type Constructor = new (...args: any) => any;
function nameMixin(Base: Constructor) {
return class extends Base {
#name?: string;
name() {
return this.#name;
}
setName(name: string) {
this.#name = name;
}
};
}
class None {}
class Foo extends nameMixin(None) {
constructor(private value: number) {
super();
}
}
const foo = new Foo(10)
foo.setName("john")
foo.name()
这似乎工作得很好,但我特别不喜欢这个技巧的一件事是我必须扩展任意
None
类。
我见过其他人用
Object.assign(Foo.prototype, mixin)
实现混合,其中 mixin
是一个对象,但我不喜欢那样,因为它将混合与声明分离。
有人知道任何其他更干净的实现混合的方法吗?
,在我看来,在 JavaScript 中实现 mixin 的多种不同方式中,最通用的模式是基于 函数的 mixin,它 知道
this
上下文,因此它总是通过以下方式应用: call
到需要混合行为的对象。
除了始终处理委托/绑定
this
之外,在应用时,还可以传递不可变变量(例如字符串和数值),甚至可以传递对象引用,这些变量可以在其他类似实现的混合中用作私有共享状态以及创建具有混合行为的对象的类/工厂。
// `this` context aware function based mixin.
function withSetGetName(name = '') {
// delegated/bound `this` context.
Reflect.defineProperty(this, 'name', {
// returning the locally scoped variable.
get: () => name,
// - controlling whether and how to change
// the locally scoped (private) variable.
set: (value) => (name = value),
});
return this;
}
class Foo {
#number;
constructor(name, number) {
this.#number = number;
// applying the function based mixin.
withSetGetName.call(this, name);
}
}
// instance of class which does mixin-in.
const foo = new Foo('Jim', 10);
// a plain object with mixed-in behavior.
const bar = withSetGetName.call({});
console.log(
'foo.name =>', foo.name
);
console.log(
"foo.name = 'John' =>", (foo.name = 'John')
);
console.log(
'foo.name =>', foo.name
);
console.log(
'bar.name =>', bar.name
);
console.log(
"bar.name = 'Jane' =>", (bar.name = 'Jane')
);
console.log(
'bar.name =>', bar.name
);
.as-console-wrapper { min-height: 100%!important; top: 0; }