Skip to content

继承

一个对象继承了另一个对象的属性和方法,就是继承。

构造函数继承的原理

js 中万物皆对象,当一个构造函数在构造对象的时候,设计者希望他们共同的属性或者方法能够共享,所以在构造函数上引入了prototype原型属性,它包含一个对象,所有实例共享的属性和方法放在这里,不需要共享的属性放在构造函数里。实例对象创建的时候自动添加一个原型指针__proto__指向这个原型。在实例查找不到属性时,根据原型链指针来到这个原型上查找。实例向原型上查找属性和方法这一行为就是继承。

instanceof的原理:能在实例的原型链中找到该构造函数的原型对象,就返回true

继承的实现

假设一个有一个构造函数:

javascript
function Animal(name) {
  // 实例属性
  this.name = name || 'Animal';
  // 实例方法
  this.sleep = function () {
    console.log(this.name + '正在睡觉!');
  };
}
// 原型方法
Animal.prototype.eat = function (food) {
  console.log(this.name + '正在吃:' + food);
};

有另一个构造函数需要继承它:

一、构造函数继承

增强子类:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型

javascript
function Dog(name) {
  Animal.call(this);
  this.name = name || 'Tom';
}

// Test Code
var dog = new Dog();
console.log(dog.name);
console.log(dog.sleep());
console.log(dog.eat()); // 不存在此方法
console.log(dog instanceof Animal); // false
console.log(dog instanceof Dog); // true

问题:

  • 只能继承父类的实例属性和方法,不能继承原型属性/方法

二、原型链继承

将父类的实例作为子类的原型,解决了构造继承不继承父类原型属性/方法问题

javascript
function Cat() {}
// 子类的原型指向父类的实例
Cat.prototype = new Animal();
// 修复constructor属性
Cat.prototype.constructor = Cat;

问题:

  • 需要实例化父类,浪费了一次调用父类构造函数的资源

另一种原型链继承,直接继承原型

javascript
function Cat() {}
// 子类的原型指向父类的实例
Cat.prototype = Animal.prototype;
// 修复constructor属性
Cat.prototype.constructor = Cat;
Cat.prototype.name = 'cat';

问题:

  • 父类和子类共享prototype,修复任何一边的constructor都会有问题!
  • 不能执行父类构造函数强化子类

三、组合继承

组合原型链继承构造函数继承

javascript
function Cat() {
  Animal.call(this);
  this.name = name || 'Tom';
}
Cat.prototype = new Animal();
// 修复constructor属性
Cat.prototype.constructor = Cat;

问题:

  • 需要实例化父类,浪费了一次调用父类构造函数的资源

四、寄生继承

将父类的原型寄生在一个空构造函数Super上,再将子类的原型指向Super实例对象,解决原型链继承需要实例化父类的问题。

javascript
function Cat() {}
// 创建一个没有实例方法的类
var Super = function () {};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
// 修复constructor
Cat.prototype.constructor = Dragon;

问题:

  • 还是没有执行父类构造函数强化子类

五、寄生组合继承

结合组合继承寄生继承的好处,解决原型链继承出现的问题!

javascript
function Cat() {
  Animal.call(this);
  this.name = name || 'Tom';
}
// 创建一个没有实例方法的类
var Super = function () {};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
// 修复constructor
Cat.prototype.constructor = Dragon;

比较完美的继承,解决了其他继承方式的问题:

  • 实现了父类构造函数对子类的强化
  • 不需要实例化父类
  • 避免了共享 construtor 问题

六,拷贝继承

将父类实例的属性和方法拷贝到子类的原型上,使子类获得父类的属性和方法。违背了复用的初衷。

非构造函数的继承

通常是指利用深拷贝获得一个新对象,在新对象上添加属性/方法的继承方式。

Object.create(obj)

此方法创建一个新对象,将传入的obj作为新对象__proto__的属性值。

即:当新对象查找不到对应的属性值时,将会进入到obj进行查找,如此实现了继承关系。