自从有了ES6的继承后,ES5的继承也退出了舞台,在实际开发也不会用得着;

先看看ES6的继承

  1. 1 class Father{
  2. 2
  3. 3 constructor(a){
  4. 4 console.log(a);
  5. 5 }
  6. 6 play(){
  7. 7 console.log("aaa");
  8. 8 }
  9. 9 static run(){
  10. 10 console.log("static");
  11. 11 }
  12. 12 }
  13. 13 class Son extends Father{
  14. 14 constructor(){
  15. 15 super();
  16. 16 }
  17. 17 }
  18. 18 var s=new Son();
  19. 19 s.play();
  20. 20 Father.run()
  21. 21 Son.run();

在ES6里只需要使用extends和super关键字即可继承父类的方法和属性(包括静态)

在ES5里没有这些关键字

ES5的继承

ES5的五种种继承方式:

  1. 对象冒充继承
  2. 原型链继承
  3. 组合继承
  4. 原型式继承
  5. 寄生式继承(重要)
  • 对象冒充继承
  1. 1 function Father(_r){
  2. 2 this.r=_r;
  3. 3 console.log("aa");
  4. 4 console.log(this.r);
  5. 5 }
  6. 6 Father.a=3;
  7. 7 Father.run=function(){
  8. 8 console.log(Box.a);
  9. 9 }
  10. 10 function Son(){
  11. 11 Father.call(this,3);//改变this的指向,执行父类构造函数并传参到父类
  12. 12 }
  13. 13 var b=new Son();//"aa",3
  14. 14 b.run();//TypeError

通过call或apply改变this指向,并执行了父类的构造函数

缺点:只能继承超类的构造函数,无法继承原型链上的方法

  • 原型链继承
  1. 1 function Father(){
  2. 2 console.log("aa");
  3. 3 }
  4. 4 Father.prototype.b=10;
  5. 5 Father.prototype.play=function(){
  6. 6 console.log(this.b);
  7. 7 }
  8. 8 Son.prototype=new Father();
  9. 9 function Son(){
  10. 10 }
  11. 11 var b=new Son();
  12. 12 b.play();//10

将父类的实例化对象赋值给子类的原型上实现的继承

缺点:覆盖子类原有的属性和方法,只能执行父类的属性和方法,无法执行父类的构造函数

  • 组合继承

  前面的两种继承(冒充,原型链)各有特点,把这两种继承组合起来称为组合继承

  1. 1 function Father(_r){
  2. 2 this.r=_r;
  3. 3 console.log("aa");
  4. 4 }
  5. 5 function Son(_r){
  6. 6 Father.call(this,_r);//冒充,改变父类的this指向子类
  7. 7 }
  8. 8 Son.prototype=new Father(3);//原型链继承
  9. 9 var c=new Son(10);
  10. 10

使用原型链继承了父类的属性和方法,使用对象冒充继承了父类的构造函数

看起来很不错的样子,但这并不是完美的继承方式;

缺点:会覆盖子类原有的属性和方法,因为原型链继承会将父类实例化,提前执行了一次父类构造函数;当子类实例化对象后,实际上是执行了两次父类的构造函数。

使用场景:子类原本没有属性和方法,父类构造函数没有内容。

  • 原型式继承

为了解决执行两次父类构造函数使用了一个中介,在继承时就不会执行父类的构造函数

  1. 1 function Father(_a){
  2. 2 this.a=_a
  3. 3 }
  4. 4 Father.prototype.play=function(){
  5. 5 console.log("aaa");
  6. 6 }
  7. 7 function Agent(){
  8. 8
  9. 9 }
  10. 10 Agent.prototype=Father.prototype;
  11. 11 function Son(){
  12. 12
  13. 13 }
  14. 14 Son.prototype=new Agent();
  15. 15 var o=new Son();
  16. 16 o.play();//aaa

使用了Agent的类作为中介,将父类的原型复制后,再进行实例化继承不会执行父类的构造函数;

缺点:虽然解决了构造函数执行两次的问题,但是使用该方法继承后,构造函数一次也不会执行。

  • 寄生式继承(完美继承)

封装了一个extend方法,该方法传入两个参数,分别是父类和子类

  1. 1 function extend(subClass, supClass) {
  2. 2 function Agent() {}
  3. 3 Agent.prototype = supClass.prototype;
  4. 4 var o = subClass.prototype;
  5. 5 subClass.prototype = new Agent();
  6. 6 if (Object.assign) {
  7. 7 Object.assign(subClass.prototype, o);
  8. 8 } else {
  9. 9 if (Object.getOwnPropertyNames) {
  10. 10 var names = Object.getOwnPropertyNames(o);
  11. 11 for (var i = 0; i < names.length; i++) {
  12. 12 var desc = Object.getOwnPropertyDescriptor(names[i]);
  13. 13 Object.defineProperty(subClass.prototype, names[i], desc);
  14. 14 }
  15. 15 } else {
  16. 16 for (var prop in o) {
  17. 17 subClass.prototype[prop] = o[prop];
  18. 18 }
  19. 19 }
  20. 20 }
  21. 21 subClass.prototype.constructor = subClass; //防止子类的构造函数被覆盖
  22. 22 if (supClass.prototype.constructor === Object) {
  23. 23 supClass.prototype.constructor = supClass; //防止父类类的构造函数被覆盖
  24. 24 }
  25. 25 // 存储父类,方便继承构造函数调用
  26. 26 subClass.prototype.superClass = supClass;
  27. 27 }
  28. 28 //调用
  29. 29 function Father(_r) {
  30. 30 this.r = _r;
  31. 31 console.log("Father");
  32. 32 }
  33. 33 Father.prototype.play = function () {
  34. 34 console.log("play game");
  35. 35 };
  36. 36 function Ball(_r) {
  37. 37 this.superClass.call(this, _r);
  38. 38 }
  39. 39 40 var s = new Son(10);//Father
  40. 41 s.play();//play game

extend方法,使用了Object.assgin、Object.getOwnPropertyNames、Object.getOwnPropertyDescriptor、Object.defineProperty都存在兼容问题,所以进行了判断。

该方法继承集合了前四种的优点,实现了ES5的完美继承;

 

结语:

ES5对比ES6的继承,麻烦太多太多,以后的实际工作也不会使用;

但是在面试的时候,面试官可能会问,多学一点总没错。

版权声明:本文为cpfblogs原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/cpfblogs/p/12906299.html