我可能不会使用class 语法定义混合。我将它们定义为对象:
const myMixin = {
doThis() {
// ...
},
doThat() {
// ...
},
// ...
};
class 语法的一个问题是super 可能无法按您预期的那样工作,因为即使在被复制之后,这些方法仍然会引用它们的原始 home 对象。 (或者这可能是你所期望的,在这种情况下你很好。)下面有更多内容。
但是如果你想使用class 语法,你可以定义一个类似Object.assign 的函数,通过Object.defineProperties 和Object.getOwnPropertyDescriptors 应用整个链中的所有方法和其他属性,这将复制getter和二传手。类似的东西(即开即用,未经测试):
function assignAll(target, source, inherited = false) {
// Start from the prototype and work upward, so that overrides work
let chain;
if (inherited) {
// Find the first prototype after `Object.prototype`
chain = [];
let p = source;
do {
chain.unshift(p);
p = Object.getPrototypeOf(p);
} while (p && p !== Object.prototype);
} else {
chain = [source];
}
for (const obj of chain) {
// Get the descriptors from this object
const descriptors = Object.getOwnPropertyDescriptors(obj);
// We don't want to copy the constructor or __proto__ properties
delete descriptors.constructor;
delete descriptors.__proto__;
// Apply them to the target
Object.defineProperties(target, descriptors);
}
return target;
}
使用它:
assignAll(Example.prototype, Mixin.prototype);
现场示例:
function assignAll(target, source, inherited = false) {
// Start from the prototype and work upward, so that overrides work
let chain;
if (inherited) {
// Find the first prototype after `Object.prototype`
chain = [];
let p = source;
do {
chain.unshift(p);
p = Object.getPrototypeOf(p);
} while (p && p !== Object.prototype);
} else {
chain = [source];
}
for (const obj of chain) {
// Get the descriptors from this object
const descriptors = Object.getOwnPropertyDescriptors(obj);
// We don't want to copy the constructor or __proto__ properties
delete descriptors.constructor;
delete descriptors.__proto__;
// Apply them to the target
Object.defineProperties(target, descriptors);
}
return target;
}
class Example {
method() {
console.log("this is method");
}
}
const mixinFoos = new WeakMap();
class Mixin {
mixinMethod() {
console.log("mixin method");
}
get foo() {
let value = mixinFoos.get(this);
if (value !== undefined) {
value = String(value).toUpperCase();
}
return value;
}
set foo(value) {
return mixinFoos.set(this, value);
}
}
assignAll(Example.prototype, Mixin.prototype, true);
const e = new Example();
e.foo = "hi";
console.log(e.foo);
// HI
这是一个示例,其中 mixin 是一个子类并使用super,只是为了演示super 在该上下文中的含义:
function assignAll(target, source, inherited = false) {
// Start from the prototype and work upward, so that overrides work
let chain;
if (inherited) {
// Find the first prototype after `Object.prototype`
chain = [];
let p = source;
do {
chain.unshift(p);
p = Object.getPrototypeOf(p);
} while (p && p !== Object.prototype);
} else {
chain = [source];
}
for (const obj of chain) {
// Get the descriptors from this object
const descriptors = Object.getOwnPropertyDescriptors(obj);
// We don't want to copy the constructor or __proto__ properties
delete descriptors.constructor;
delete descriptors.__proto__;
// Apply them to the target
Object.defineProperties(target, descriptors);
}
return target;
}
class Example {
method() {
console.log("this is Example.method");
}
}
class MixinBase {
method() {
console.log("this is MixinBase.method");
}
}
class Mixin extends MixinBase {
method() {
super.method();
console.log("this is Mixin.method");
}
}
assignAll(Example.prototype, Mixin.prototype, true);
const e = new Example();
e.method();
// "this is MixinBase.method"
// "this is Mixin.method"
你说过你想使用类 instances 作为 mixins。上面的工作很好。这是一个例子:
function assignAll(target, source, inherited = false) {
// Start from the prototype and work upward, so that overrides work
let chain;
if (inherited) {
// Find the first prototype after `Object.prototype`
chain = [];
let p = source;
do {
chain.unshift(p);
p = Object.getPrototypeOf(p);
} while (p && p !== Object.prototype);
} else {
chain = [source];
}
for (const obj of chain) {
// Get the descriptors from this object
const descriptors = Object.getOwnPropertyDescriptors(obj);
// We don't want to copy the constructor or __proto__ properties
delete descriptors.constructor;
delete descriptors.__proto__;
// Apply them to the target
Object.defineProperties(target, descriptors);
}
return target;
}
class Example {
method() {
console.log("this is Example.method");
}
}
class MixinBase {
method() {
console.log("this is MixinBase.method");
}
}
const mixinFoos = new WeakMap();
class Mixin extends MixinBase {
constructor(value) {
super();
this.value = value;
}
mixinMethod() {
console.log(`mixin method, value = ${this.value}`);
}
get foo() {
let value = mixinFoos.get(this);
if (value !== undefined) {
value = String(value).toUpperCase();
}
return value;
}
set foo(value) {
return mixinFoos.set(this, value);
}
method() {
super.method();
console.log("this is Mixin.method");
}
}
// Here I'm using it on `Example.prototype`, but it could be on an
// `Example` instance as well
assignAll(Example.prototype, new Mixin(42), true);
const e = new Example();
e.mixinMethod();
// "mixin method, value = 42"
e.method();
// "this is MixinBase.method"
// "this is Mixin.method"
e.foo = "hi";
console.log(e.foo);
// "HI"
但实际上,您可以随心所欲地设计它; assignAll 只是一个例子,上面的可运行的也是如此。这里的关键是:
-
使用Object.getOwnPropertyDescriptors 获取属性描述符和Object.defineProperties(或它们的单数对应物,getOwnPropertyDescriptor 和defineProperty),以便访问器方法作为访问器传输。
-
从基础原型一直到实例,这样每个级别的覆盖都可以正常工作。
-
super 将继续在其原始继承链中工作,而不是在 mixin 已复制到的新位置。