JavaScript中的多重继承/原型
我已经到了需要在JavaScript中发生某种基本的多重继承的地步。 (我不是在这里讨论这是不是一个好主意,所以请将这些评论留给自己。)
我只想知道是否有人试图取得任何(或不)成功,以及他们如何去做。
为了解决这个问题,我真正需要的是能够拥有一个能够从多个原型链继承属性的对象(即每个原型可以拥有自己的链),但是按照给定的优先顺序(它会搜索链为了第一个定义)。
为了证明这在理论上是可能的,可以通过将次链连接到主链的末端来实现,但这会影响所有以前原型的任何实例,这不是我想要的。
思考?
编辑欣赏响应人员,但虽然共识似乎是静态复制两棵树的属性,这在大多数情况下都可行(并且很可能是我最终做的),但我最感兴趣的是动态解决方案,允许单独的原型链被改变,并且仍然具有由实例“拾起”的那些改变。
Mixins可以用于JavaScript,以实现您可能想通过多重继承来解决的相同目标。
使用代理对象可以在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
我使用代理,将基本操作重定向到合适的对象。
我使用这些陷阱:
has
陷阱是对陷阱in
操作。 我使用some
来检查是否至少有一个原型包含该属性。 get
陷阱是获得财产价值的陷阱。 我使用find
来查找包含该属性的第一个原型,然后返回该值,或者在适当的接收方上调用getter。 这由Reflect.get
处理。 如果没有原型包含该属性,则返回undefined
。 set
陷阱是设置属性值的陷阱。 我使用find
来查找包含该属性的第一个原型,并在适当的接收器上调用它的setter。 如果没有setter或没有原型包含该属性,则在适当的接收器上定义该值。 这由Reflect.set
处理。 enumerate
陷阱是for...in
循环中的陷阱。 我从第一个原型迭代枚举属性,然后从第二个原型迭代,依此类推。 一旦一个属性被迭代后,我将它存储在一个散列表中,以避免再次迭代它。 警告 :此陷阱已在ES7草案中被删除,并在浏览器中弃用。
ownKeys
陷阱是Object.getOwnPropertyNames()
的陷阱。 自ES7以来, for...in
循环不断调用[[GetPrototypeOf]]并获取每个属性的属性。 所以为了使它迭代所有原型的属性,我使用这个陷阱来使所有可枚举的继承属性看起来像自己的属性。 getOwnPropertyDescriptor
陷阱是Object.getOwnPropertyDescriptor()
的陷阱。 让所有可枚举的属性在自己的ownKeys
显示为自己的属性是不够的, for...in
循环将获取描述符来检查它们是否可枚举。 因此,我使用find
来查找包含该属性的第一个原型,并重复其原型链直到找到属性所有者,然后返回其描述符。 如果没有原型包含该属性,则返回undefined
。 描述符被修改以使其可配置,否则我们可能会破坏一些代理不变量。 preventExtensions
和defineProperty
陷阱以防止这些操作修改代理目标。 否则,我们最终可能会破坏一些代理不变量。 有更多的陷阱可用,我不使用
getPrototypeOf
陷阱,但没有正确的方法来返回多个原型。 这意味着instanceof
也不会工作。 因此,我让它得到目标的原型,最初为空。 setPrototypeOf
陷阱并接受一组对象,这些对象将取代原型。 这留给读者一个练习。 在这里,我只是让它修改目标的原型,这没什么用处,因为没有陷阱使用目标。 deleteProperty
陷阱是删除自己的属性的陷阱。 代理代表继承,所以这没有多大意义。 我让它试图删除目标,无论如何应该没有财产。 isExtensible
陷阱是获得可扩展性的陷阱。 鉴于不变量迫使它返回与目标相同的可扩展性,它并没有多大用处。 所以我只是让它将操作重定向到可扩展的目标。 apply
和construct
陷阱是调用或实例化的陷阱。 它们仅在目标是函数或构造函数时才有用。 例
// 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"
多重继承[编辑,不是适当的类型继承,而是属性的适当继承; mixins]在JavaScript中是非常简单的,如果你使用构造的原型而不是通用对象。 这里有两个要继承的父类:
function FoodPrototype() {
this.eat = function () {
console.log("Eating", this.name);
};
}
function Food(name) {
this.name = name;
}
Food.prototype = new FoodPrototype();
function PlantPrototype() {
this.grow = function () {
console.log("Growing", this.name);
};
}
function Plant(name) {
this.name = name;
}
Plant.prototype = new PlantPrototype();
请注意,我在每种情况下都使用了相同的“姓名”成员,如果父母不同意应如何处理“姓名”,这可能会成为问题。 但在这种情况下它们是兼容的(多余的,真的)。
现在我们只需要一个从两个继承的类。 通过调用原型和对象构造函数的构造函数(不使用new关键字)来完成继承。 首先,原型必须从父原型继承
function FoodPlantPrototype() {
FoodPrototype.call(this);
PlantPrototype.call(this);
// plus a function of its own
this.harvest = function () {
console.log("harvest at", this.maturity);
};
}
构造函数必须从父构造函数继承:
function FoodPlant(name, maturity) {
Food.call(this, name);
Plant.call(this, name);
// plus a property of its own
this.maturity = maturity;
}
FoodPlant.prototype = new FoodPlantPrototype();
现在,您可以种植,种植和收获不同的实例:
var fp1 = new FoodPlant('Radish', 28);
var fp2 = new FoodPlant('Corn', 90);
fp1.grow();
fp2.grow();
fp1.harvest();
fp1.eat();
fp2.harvest();
fp2.eat();
链接地址: http://www.djcxy.com/p/27235.html
上一篇: Multiple inheritance/prototypes in JavaScript
下一篇: Relation between [[Prototype]] and prototype in JavaScript