

1. 原型链继承
过程:①定义父类构造函数 ➡ ②给父类的原型添加方法 ➡ ③定义子类型的构造函数 ➡④将创建的父类型对象赋值给子类型的原型 ➡ ⑤将子类型的构造属性设置为子类型 ➡ ⑥给子类型的原型添加方法 ➡ ⑦创建子类型的对象,可以调用父类型的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   | function People () {   this.pName = '人类' } People.prototype.getPName = function () {   console.log(this.pName);
  }
  function Student () {   this.sName = '学生' }
  Student.prototype = new People() Student.prototype.constructor = Student
  Student.prototype.getSName = function () {   console.log(this.sName); }
  var s = new Student() console.log(s);
  var p = new People() console.log(p)
  | 
 
执行结果:


存在的问题
- 引用类型的属性被所有实例共享
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | function Parent () {     this.names = ['kevin', 'daisy']; }
  function Child () { }
  Child.prototype = new Parent();
  var child1 = new Child();
  child1.names.push('yayu');
  console.log(child1.names); 
  var child2 = new Child();
  console.log(child2.names); 
  | 
 
- 在创建子类型实例时,不能向父类型传参
 
2.借用构造函数继承(假继承)
过程:①定义父类型构造函数 ➡ ②定义子类型的构造函数 ➡ ③在子类型构造函数中调用父类型构造
关键点:在子类型构造函数中通过call调用父类型的构造函数
1 2 3 4 5 6 7 8 9 10 11 12
   | function People (name, age) {   this.name = name;   this.age = age; }
  function Student (name, age, price) {   People.call(this, name, age);    this.price = price; }
  var s = new Student('Tom', 12, 13000); console.log(s); 
  | 
 
优点
- 避免了引用类型的属性被所有实例共享
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | function Parent () {     this.names = ['kevin', 'daisy']; }
  function Child () {     Parent.call(this); }
  var child1 = new Child();
  child1.names.push('yayu');
  console.log(child1.names); 
  var child2 = new Child();
  console.log(child2.names); 
  | 
 
- 在创建子类型实例时,可以向父类型传参
 
缺点
方法在构造函数中定义,每次创建实例都会创建一遍
3.组合继承(原型链继承 + 借用构造函数继承)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | function People (name, age) {   this.name = name;   this.age = age; } People.prototype.setName = function (name) {   this.name = name; }
  function Student (name, age, price) {   People.call(this, name, age);   this.price = price; } Student.prototype = new People(); Student.prototype.constructor = Student;
  Student.prototype.setPrice = function (price) {   this.price = price; }
  var s = new Student('Tom', 12, 3500); s.setName('Jack'); s.setPrice(1500); console.log(s); 
  | 
 
优点
融合继承原型链继承和构造函数继承的优点,是JavaScript中最常用的继承模式。
4.原型式继承
原型式继承适用于这种情况:你有一个对象,想在它的基础上再创建一个新的对象。你需要把这个对象先传给object(),然后再对返回的对象进行适当的修改。
1 2 3 4 5
   | function object(o){   function F(){}   F.prototype = o;   return new F(); }
  | 
 
※ 本质上,object()是对传入的对象执行了一次浅复制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | function object (o) {   function F () { }   F.prototype = o;   return new F(); }
  var person = {   name: 'Jack',   frineds: ['Daming', 'Lingling'] }
  var p1 = object(person); var p2 = object(person);
  p1.name = 'Sam'; console.log(p2.name); 
  p1.frineds.push('Amy'); console.log(p2.frineds); 
  | 
 
※ 修改p1.name的值,p2.name的值并未发生改变,并不是因为p1和p2有独立的name值,而是因为p1.name= 'Sam' 是给p1添加了name的属性,并赋值为Sam,并非修改了原型上name的值。
缺点
包含引用类型的属性值会被所有实例共享(与原型链继承相同)。
5.寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来做增强对象,最后返回对象。
1 2 3 4 5 6 7
   | function createAnother(original){   let clone = object(original);   clone.sayHi = function(){     console.log('Hi');   }   return clone; }
  | 
 
缺点
与借用构造函数继承模式相同,每次创建对象都会创建一遍方法。
6.寄生组合式继承
组合继承的方式也存在效率的问题,最主要的效率问题就是父类构造函数始终会被调用两次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | function Parent (name) {     this.name = name;     this.colors = ['red', 'blue', 'green']; }
  Parent.prototype.getName = function () {     console.log(this.name) }
  function Child (name, age) {     Parent.call(this, name);      this.age = age; }
  Child.prototype = new Parent(); 
  var child1 = new Child('kevin', '18');
  console.log(child1)
  | 
 
该如何精益求精,避免重复调用呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   | function Parent (name) {     this.name = name;     this.colors = ['red', 'blue', 'green']; }
  Parent.prototype.getName = function () {     console.log(this.name) }
  function Child (name, age) {     Parent.call(this, name);     this.age = age; }
 
  var F = function () {}; F.prototype = Parent.prototype; Child.prototype = new F();
 
  var child1 = new Child('kevin', '18'); console.log(child1);
  | 
 
封装一下这个继承方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | function object(o) {     function F() {}     F.prototype = o;     return new F(); }
  function prototype(child, parent) {     var prototype = object(parent.prototype);     prototype.constructor = child;     child.prototype = prototype; }
 
  prototype(Child, Parent);
  | 
 
这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。  ——《JavaScript高级程序设计》