几种基于原型链的继承方式


ECMASCript只支持实现继承,主要是依靠原型链来实现的。

实现一个简单的原型链

//SuperType类型
function SuperType(){
  this.property = true;
}
SuperType.prototype.getSuperValue = function (){
  return this.property
}
//SubType类型
function SubType(){
  this.subproperty = false;
}
// 重写了原型对象,SubType继承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
  return this.subproperty;
}
let instance = new SubType();
// 注意instance能使用SuperValue的方法
console.log(instance.getSuperValue())//true

所有引用类型默认继承了Object,而所有函数的默认原型都是Object的实例。

console.log(instance instanceof Object);//true
console.log(instance instanceof SuperType);//true
console.log(instance instanceof SubType);//true

也可以使用isPrototypeOf()方法

console.log(Object.prototype.isPrototypeOf(instance));
console.log(SuperType.prototype.isPrototypeOf(instance));
console.log(SubType.prototype.isPrototypeOf(instance));

注意通过原型链实现继承的时候,不要使用对象字面量创建原型方法。因为这样就会重写原型链,导致继承关系失效。

原型链第一个问题就是原型链会导致继承的prototype的属性会“共享”,实例之间会相互影响,第二问题就是无法在不影响其他实例情况下给超类的构造函数传参。实际上很少单独使用原型链。

1.组合继承

function SuperType(name){
  this.name=name;
  this.color=['red','blue','green'];
}
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.constructor = SubType;
SubType.prototype.sayAge = function (){
  console.log(this.age)
};

let instance1 = new SubType("nick", 29);
instance1.color.push("black");
console.log(instance1.color);
instance1.sayName();
instance1.sayAge();

let instance2 = new SubType("grep", 27);
console.log(instance2.color);
instance2.sayName();
instance2.sayAge();

2.原型式继承

// 对参数o进行一次浅复制
function object(o){
  function F(){}
  F.prototype = o;
  return new F()
}
// 作为另一个对象的基础
let person = {
  name:"nick",
  friends:['shellby','court','van']
};
let person2 = object(person);
person2.name = 'grep';
person2.friends.push("Rob");

let person3 = object(person);
person3.name = 'lina';
person3.friends.push("Barbie");

console.log(person.friends)//[ 'shellby', 'court', 'van', 'Rob', 'Barbie' ]

person2和person3有着与person一样的共享属性

ES5中使用Object.create来替代object方法,并且可以传入第二个参数,一个为新对象定义额外属性的对象。

3.寄生式继承

function createAnother(ori){
  // 创建一个新对象
  let clone = Object.create(ori);
  // 增强这个对象
  clone.sayHi = function (){
    console.log("hi")
  };
  return clone
}
let person = {
  name:"nick",
  friends:['shell','court','van']
};
let person1 = createAnother(person);
person1.sayHi();

4 寄生组合式继承

组合继承的缺点就是会两次调用父类的构造函数,可以直接使用父类原型的复制副本。

// 子类构造函数和父类构造函数
function inheritPrototype(subType,superType){
  let prototype = Object.create(superType.prototype);
  prototype.constructor = subType;
  subType.prototype = prototype
}
function SuperType(name){
  this.name = name;
  this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function (){
  console.log(this.name)
}
function SubType(name,age){
  SuperType.call(this,name)
  this.age = age
}
// 实际上就是复制父类的prototype方法
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function (){
  console.log(this.age)
}
let instance = new SubType('nick', '29')
console.log(instance.colors,instance.name,instance.age)
instance.sayAge()
let instance2 = new SubType('grep', '27')
console.log(instance2.colors,instance2.name,instance2.age)
instance2.sayAge()

这样只需要每次调用一次构造函数就行了。

相关