在 ECMAScript 6 中,通过使用代理对象可以实现多重继承。
实现
function getDesc (obj, prop) {
var desc = Object.getOwnPropertyDescriptor(obj, prop);
return desc || (obj=Object.getPrototypeOf(obj) ? getDesc(obj, prop) : void 0);
}
function multiInherit (...protos) {
return Object.create(new Proxy(Object.create(null), {
has: (target, prop) => protos.some(obj => prop in obj),
get (target, prop, receiver) {
var obj = protos.find(obj => prop in obj);
return obj ? Reflect.get(obj, prop, receiver) : void 0;
},
set (target, prop, value, receiver) {
var obj = protos.find(obj => prop in obj);
return Reflect.set(obj || Object.create(null), prop, value, receiver);
},
*enumerate (target) { yield* this.ownKeys(target); },
ownKeys(target) {
var hash = Object.create(null);
for(var obj of protos) for(var p in obj) if(!hash[p]) hash[p] = true;
return Object.getOwnPropertyNames(hash);
},
getOwnPropertyDescriptor(target, prop) {
var obj = protos.find(obj => prop in obj);
var desc = obj ? getDesc(obj, prop) : void 0;
if(desc) desc.configurable = true;
return desc;
},
preventExtensions: (target) => false,
defineProperty: (target, prop, desc) => false,
}));
}
解释
代理对象由目标对象和一些陷阱组成,这些陷阱定义基本操作的自定义行为。
当创建从另一个对象继承的对象时,我们使用 .但是在这种情况下,我们想要多重继承,因此我使用的代理不是将基本操作重定向到适当的对象。Object.create(obj)
obj
我使用这些陷阱:
还有更多可用的陷阱,我不使用它
- 可以添加
getPrototypeOf
陷阱,但没有正确的方法来返回多个原型。这意味着也行不通。因此,我让它获取目标的原型,该原型最初是空的。instanceof
- 可以添加
setPrototypeOf
陷阱并接受一个对象数组,这将替换原型。这是留给读者的练习。在这里,我只是让它修改目标的原型,这并没有多大用处,因为没有陷阱使用目标。
删除属性
陷阱是用于删除自己的属性的陷阱。代理表示继承,因此这没有多大意义。我让它尝试在目标上删除,无论如何都应该没有属性。
isExtensible
陷阱是用于获取可扩展性的陷阱。没有多大用处,因为不变性迫使它返回与目标相同的可扩展性。因此,我只是让它将操作重定向到目标,这将是可扩展的。
应用
和构造
陷阱是用于调用或实例化的陷阱。仅当目标是函数或构造函数时,它们才有用。
例
// Creating objects
var o1, o2, o3,
obj = multiInherit(o1={a:1}, o2={b:2}, o3={a:3, b:3});
// Checking property existences
'a' in obj; // true (inherited from o1)
'b' in obj; // true (inherited from o2)
'c' in obj; // false (not found)
// Setting properties
obj.c = 3;
// Reading properties
obj.a; // 1 (inherited from o1)
obj.b; // 2 (inherited from o2)
obj.c; // 3 (own property)
obj.d; // undefined (not found)
// The inheritance is "live"
obj.a; // 1 (inherited from o1)
delete o1.a;
obj.a; // 3 (inherited from o3)
// Property enumeration
for(var p in obj) p; // "c", "b", "a"