继承的实现方式之 组合继承
基本形式
通过原型链继承原型属性及方法,通过假借构造函数继承实例属性及方法。
function SuperType(name){ this.name = name; this.colors = ["red", "blue"]; } SuperType.prototype.sayName = function(){ console.log(this.name); } function SubType(name, age){ SuperType.call(this, name); this.age = age; } SubType.prototype = new SuperType(); SubType.prototype.sayAge = function(){ console.log(this.age); } let instance1 = new SubType("Jack", 19); instance1.colors.push("black"); console.log(instance1.colors);//["red", "blue", "black"] instance1.sayName();//Jack instance1.sayAge();//19 let instance2 = new SubType("Ann", 29); instance2.colors.push("pink"); console.log(instance2.colors);//["red", "blue", "pink"] instance2.sayName();//Ann instance2.sayAge();//29 delete instance1.colors; delete instance2.name; console.log(instance1.colors); // ["red", "blue"] 是原型SuperType实例上的colors console.log(Object.getOwnPropertyNames(instance1));//["name", "age"]
优点:结合了原型链继承和假借构造函数继承的优点
- 可以继承父类的原型属性及方法
- 可以形成原型链,使得子类实例和父类原型间iinstanceof和isPropertyOf判断为真
- 实例之间不用共享属性
- 可以向父类构造函数传参
缺点
SuperType构造函数被调用了两次,第一次调用在改变子类原型时,把一个父类实例设为了子类原型;第二次调用在通过new调用子类构造函数时,假借了父类的构造函数继承父类的实例属性。
但在第一次调用以后,父类的实例属性和方法实际成为了子类的原型属性和方法,只是在创建对象假借构造函数时,这些原型方法被同名的实例方法隐藏了,实际访问不到。因此,始终有一套访问不到的属性在子类的原型上,造成了空间的浪费。并且如果我们想删除子类实例上的同名属性,会发现删除以后该属性仍然能够访问到,因为原型上的属性仍然存在。