原型链继承

套路

  • 定义父类型的构造函数
  • 给父类型的原型添加方法
  • 定义子类型的构造函数
  • 创建父类型的实例对象赋值给子类型的原型
  • 让子类型的原型对象上的 constructor 指向子类型构造函数
  • 给子类型的原型添加方法
  • 创建子类型的实例对象,可以调用父类型的方法

关键:子类型的原型 = 父类型的实例对象

  1. // 父类型
  2. function Supper(params) {
  3. this.supper = 'supper';
  4. }
  5. Supper.prototype.showSupper = function(params) {
  6. console.log(this.supper);
  7. };
  8. // 子类型
  9. function Sub(params) {
  10. this.sub = 'sub';
  11. }
  12. // 子类型的原型为父类型的一个实例对象,继承父类型方法
  13. Sub.prototype = new Supper();
  14. // 子类型的原型对象的constructor 指向子类型构造函数
  15. Sub.prototype.constructor = Sub;
  16. Sub.prototype.showSub = function(params) {
  17. console.log(this.sub);
  18. };
  19. var sub = new Sub();
  20. sub.showSub(); // sub
  21. sub.showSupper(); // supper

图解:
image.png

组合继承(原型链继承+借用构造函数继承)

  • 利用原型链继承实现继承父类型对象的方法
  • 利用call() 借用父类型构造函数初始化相同属性
  • 组合继承在 Student.prototype = new Person();这一步时,会执行 new Person()方法,这里没有必要,引出寄生组合继承 ```javascript // 父类型 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.setName = function(name) { this.name = name; }; // 子类型 function Student(name, age, id) { Person.call(this, name, age); // 借用Person 得到name和age属性,call改变this指向 this.id = id; } // 子类型的原型为父类型的一个实例对象,继承父类型方法 Student.prototype = new Person(); // 子类型的原型对象的constructor 指向子类型构造函数 Student.prototype.constructor = Student; Student.prototype.setId = function(id) { this.id = id; };

var s = new Student(‘xq’, 22, 111); s.setName(‘tzq’); s.setId(14); console.log(s.name, s.age, s.id); // tzq 22 14

  1. <a name="B3gBg"></a>
  2. # 寄生组合继承(最理想的方式)
  3. 使用 Object.create(Sup.prototype) 方法返回一个以Person.prototype 为原型的对象,而不用执行 new Person() 方法。
  4. `Student.prototype = Object.create(Person.prototype);`<br />**Object.create也就是说,可以把`Student.prototype`的`__proto__`(隐式原型)指向`Person.prototype`**。
  5. 这种方式的高效率体现它只调用了一次Parent构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。——引用《JavaScript高级程序设计》
  6. ```javascript
  7. // 父类型
  8. function Person(name, age) {
  9. this.name = name;
  10. this.age = age;
  11. }
  12. Person.prototype.setName = function(name) {
  13. this.name = name;
  14. };
  15. // 子类型
  16. function Student(name, age, id) {
  17. Person.call(this, name, age); // 借用Person 得到name和age属性,call改变this指向
  18. this.id = id;
  19. }
  20. // 子类型的原型指向返回一个以Person.prototype为原型的对象
  21. Student.prototype = Object.create(Person.prototype);
  22. // 子类型的原型对象的constructor 指向子类型构造函数
  23. Student.prototype.constructor = Student;
  24. Student.prototype.setId = function(id) {
  25. this.id = id;
  26. };
  27. var s = new Student('xq', 22, 111);
  28. s.setName('tzq');
  29. s.setId(14);
  30. console.log(s.name, s.age, s.id);
  31. // tzq 22 14

红宝书寄生组合继承

  1. function inherit(child, parent) {
  2. const prototype = Object.create(parent.prototype) // 创建对象
  3. prototype.constructor = child // 增强对象
  4. child.prototype = prototype // 赋值对象
  5. }

寄生组合继承:ES6的extends继承

  1. // ES6
  2. class Parent{
  3. constructor(name){
  4. this.name = name;
  5. }
  6. static sayHello(){
  7. console.log('hello');
  8. }
  9. sayName(){
  10. console.log('my name is ' + this.name);
  11. return this.name;
  12. }
  13. }
  14. class Child extends Parent{
  15. constructor(name, age){
  16. super(name);
  17. this.age = age;
  18. }
  19. sayAge(){
  20. console.log('my age is ' + this.age);
  21. return this.age;
  22. }
  23. }
  24. let parent = new Parent('Parent');
  25. let child = new Child('Child', 18);
  26. console.log('parent: ', parent); // parent: Parent {name: "Parent"}
  27. Parent.sayHello(); // hello
  28. parent.sayName(); // my name is Parent
  29. console.log('child: ', child); // child: Child {name: "Child", age: 18}
  30. Child.sayHello(); // hello
  31. child.sayName(); // my name is Child
  32. child.sayAge(); // my age is 18

两条:原型链

  1. // 1、构造器原型链
  2. Child.__proto__ === Parent; // true
  3. Parent.__proto__ === Function.prototype; // true
  4. Function.prototype.__proto__ === Object.prototype; // true
  5. Object.prototype.__proto__ === null; // true
  6. // 2、实例原型链
  7. child.__proto__ === Child.prototype; // true
  8. Child.prototype.__proto__ === Parent.prototype; // true
  9. Parent.prototype.__proto__ === Object.prototype; // true
  10. Object.prototype.__proto__ === null; // true

如图:
es6-extends@lxchuan12.dded51bd.png
结合代码和图片:ES6 extends继承,过程:

  • 把子类构造函数(Child)的隐式原型(__proto__)指向了父类构造函数(Parent)
  • 把子类实例对象(child)的原型对象(Child.prototype)的隐式原型(__proto__)指向父类实例对象(parent)的原型对象(Parent.prototype)
  • 子类构造函数(Child)继承了父类构造函数(Parent)里的方法,使用super调用(ES5则用call或者apply调用传参)

总结

寄生组合式继承是开发者使用比较多的。 回顾寄生组合式继承。主要就是三点:

  • 子类构造函数的__proto__指向父类构造器,继承父类的静态方法
  • 子类构造函数的原型对象prototype的隐式原型__proto__指向父类构造器的原型对象prototype,继承父类的方法
  • 子类构造器里调用父类构造器,继承父类的属性