实现方案
// Flyable “特性”类
function Flyable() {
this.fly = function() {
console.log('I can fly');
};
}
// Swimmable “特性”类
function Swimmable() {
this.swim = function() {
console.log('I can swim');
};
}
// Duck类通过多重继承获得Flyable和Swimmable的能力
function Duck() {
Flyable.call(this);
Swimmable.call(this);
}
// 辅助函数,用于继承原型方法
function extend(subClass, superClass) {
function F() {}
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;
}
// Duck类继承Flyable和Swimmable的原型方法
extend(Duck, Flyable);
extend(Duck, Swimmable);
// 测试
const duck = new Duck();
duck.fly();
duck.swim();
原理
- 借用构造函数(call方法):在
Duck
构造函数内部,通过Flyable.call(this)
和Swimmable.call(this)
调用Flyable
和Swimmable
的构造函数,将Flyable
和Swimmable
的实例属性复制到Duck
实例上,这样Duck
实例就拥有了fly
和swim
方法。
- 原型链继承(extend函数):通过
extend
函数,让Duck
的原型链继承自Flyable
和Swimmable
的原型,这样Duck
实例就可以访问Flyable
和Swimmable
原型上的方法,即使是未来添加到原型上的方法也能访问。
可能存在的问题
- 属性重复:由于使用
call
方法将属性复制到Duck
实例上,如果Flyable
和Swimmable
中有同名的实例属性,会导致属性覆盖,后面调用的构造函数的属性会覆盖前面的。
- 原型链复杂:多次使用
extend
函数继承原型,会使Duck
的原型链变得复杂,可能会增加查找属性和方法的时间,影响性能。同时,调试和理解代码也会变得更加困难。
- 方法重名冲突:如果
Flyable
和Swimmable
的原型方法中有同名的方法,同样会导致后面继承的方法覆盖前面的,可能会破坏预期的行为。