[关闭]
@moshangxc 2018-08-29T14:53:58.000000Z 字数 5139 阅读 711

JS继承的几种实现方式

js 继承


ECMAScript只支持实现继承,实现继承主要的依靠原型链来实现的。下面介绍几种JS实现继承的方式,以及各自的优缺点。

原型链

实现原理:利用原型让一个引用类型继承另一个引用类型的属性和方法.本质为重写原型对象。

  1. function Sup(){
  2. this.name = "sup";
  3. this.items = [1, 2, 3];
  4. }
  5. Sup.prototype.geName = function(){
  6. console.log(this.name);
  7. }
  8. function Sub(){
  9. this.age = 25;
  10. }
  11. Sub.prototype = new Sup();
  12. Sub.prototype.getAge =function(){
  13. console.log(this.age);
  14. }
  15. var instance = new Sub();
  16. instance.getName();

存在问题
1.继承包含引用类型值的原型,引用类型的原型属性会被所以实例共享。耦合度较高。
2.创建子类型的实例时不能向父类的构造函数传递 参数。

验证demo

  1. var a = new Sub();
  2. console.log(a.items); //[1, 2, 3]
  3. a.items.push(4);
  4. var b = new Sub();
  5. console.log(b.items); //[1, 2, 3, 4]

如何解决上述问题?


借用构造函数

  1. function Sup(name){
  2. this.name = name;
  3. this.items = [1, 2, 3];
  4. }
  5. Sup.getItems = function(){
  6. console.log(this.items);
  7. }
  8. function Sub(name){
  9. Sup.call(this, name);
  10. }
  11. var a = new Sub();
  12. a.items.push(4);
  13. console.log(a.items); //[1, 2, 3, 4]
  14. var b = new Sub();
  15. console.log(b.items); //[1, 2, 3]

可以解决上诉的问题,但同时也存在缺陷。

父类原型链上定义的方法对子类是不可见的,若要继承方法,则父类的方法都应定义在构造函数内部,方法都是在构造函数中定义,不能复用

如何解决?


组合继承

  1. function Sup(name){
  2. this.name = name;
  3. this.items = [1, 2, 3];
  4. }
  5. Sup.prototype.getItems = function(){
  6. console.log("items: ", this.items);
  7. }
  8. Sup.prototype.getName = function(){
  9. console.log("name: ", this.name);
  10. }
  11. function Sub(name, age){
  12. Sup.call(this, name);
  13. this.age = age;
  14. }
  15. Sub.prototype = new Sup();
  16. Sub.prototype.constructor = Sub;
  17. Sub.prototype.getAge = function(){
  18. console.log("age: ", this.age);
  19. }
  20. var a = new Sub("lilei", 25);
  21. a.items.push(4);
  22. a.getName(); //lilei
  23. a.getAge(); //25
  24. a.getItems(); //[1, 2, 3, 4]
  25. var b = new Sub("hanmeimei", 24);
  26. b.getName(); //hanmeimei
  27. b.getAge(); //24
  28. b.getItems(); //[1, 2, 3]

那么问题来了,又存在哪些缺陷呢?

构造函数和原型链上定义了重复的属性

为解决上述问题,先来看一下接下来介绍的一种模式


寄生式继承

  1. function object(sup){
  2. function F(){}
  3. F.prototype = sup;
  4. return new F();
  5. }
  6. function createObj(obj){
  7. var clone = object(obj);
  8. clone.getName = function(){
  9. console.log(this.name);
  10. }
  11. clone.getFriends = function(){
  12. console.log(this.friends);
  13. }
  14. return clone;
  15. }
  16. var obj = {
  17. name : 'lily',
  18. friends: ['lucy', 'lilei', 'hanmeimei']
  19. }
  20. var obj1 = createObj(obj),
  21. obj2 = createObj(obj);
  22. obj1.name = 'jim';
  23. obj1.friends.push('lady gaga');
  24. // obj2.friends = ['katy perry', 'rianna', 'adele'];
  25. obj1.getName(); //jim
  26. obj1.getFriends(); //['lucy', 'lilei', 'hanmeimei', 'lady gaga']
  27. obj2.getName(); //lily
  28. obj2.getFriends(); //['lucy', 'lilei', 'hanmeimei', 'lady gaga']

存在问题:含有应用类型的值始终会共享
具体原因上节课领导已经分析过,好好回忆一下下,是不是忘了(´・・)ノ(._.`)

在主要考虑对象而不是自定义类型和构造函数的情况下是有用的


寄生组合式继承

  1. function inherit(Sup, Sub){
  2. function F(){}
  3. F.prototype = Sup.prototype;
  4. var obj = new F();
  5. obj.constructor = Sub;
  6. Sub.prototype = obj;
  7. }
  1. function Sup(name){
  2. this.name = name;
  3. this.items = [1, 2, 3];
  4. }
  5. Sup.prototype.getItems = function(){
  6. console.log("items: ", this.items);
  7. }
  8. Sup.prototype.getName = function(){
  9. console.log("name: ", this.name);
  10. }
  11. function Sub(name, age){
  12. Sup.call(this, name);
  13. this.age = age;
  14. }
  15. inherit(Sup, Sub);
  16. Sub.prototype.getAge = function(){
  17. console.log("age: ", this.age);
  18. }
  19. Sub.prototype.getName = function(){
  20. console.log("nameForSub: ", this.name);
  21. }
  22. function Sub1(name, age){
  23. Sup.call(this, name);
  24. this.age = age;
  25. }
  26. inherit(Sup, Sub1);
  27. Sub1.prototype.getAge = function(){
  28. console.log("age: ", this.age);
  29. }
  30. var a = new Sub("lilei", 25);
  31. a.items.push(4);
  32. a.getName(); //nameForSub: lilei
  33. a.getAge(); //age: 25
  34. a.getItems(); //items: [1, 2, 3, 4]
  35. var b = new Sub("hanmeimei", 24);
  36. b.getName(); //nameForSub: hanmeimei
  37. b.getAge(); //age: 24
  38. b.getItems(); //items: [1, 2, 3]
  39. var c = new Sub1("jim", 23);
  40. c.getName(); //name: jim
  41. c.getAge(); //age: 23
  42. c.getItems(); //items: [1, 2, 3]

思考:

inherit函数能不能换成如下写法,有什么区别?

  1. function inherit(Sup, Sub){
  2. Sub.prototype = Sup.prototype;
  3. }

对比

继承方式 描述 好处 缺陷
原型链 重写子类原型链为基类的实例对象 易理解 1.无法给基类构造函数传递参数 2. 所有实例共享引用类型的属性
借用构造函数 子类构造函数中调用基类构造函数,作用域指向当前对象 解决原型链存在的问题 基类原型链上定义的方法属性对子类是不可见的
组合继承 原型链和借用构造函数组合 以上缺陷可以解决 存在重复定义的属性和方法
寄生式继承 构建临时构造函数,重写原型,返回新的对象 没想到,应用场景不多(~ o ~)~zZ 不适用于自定义类型,引用类型属性会共享
寄生式继承 是否必填 解决综上问题 不易理解,待消化

以上内容仅个人理解,如有错误还请谅解 (〃'▽'〃)


  1. //代码附录
  2. //1.
  3. function inherit(Sup, Sub){
  4. function F(){}
  5. F.prototype = Sup.prototype;
  6. var obj = new F();
  7. obj.constructor = Sub;
  8. Sub.prototype = obj;
  9. }
  10. function Sup(name){
  11. this.name = name;
  12. this.items = [1, 2, 3];
  13. }
  14. Sup.prototype.getItems = function(){
  15. console.log("items: ", this.items);
  16. }
  17. Sup.prototype.getName = function(){
  18. console.log("name: ", this.name);
  19. }
  20. function Sub(name, age){
  21. Sup.call(this, name);
  22. this.age = age;
  23. }
  24. inherit(Sup, Sub);
  25. Sub.prototype.getAge = function(){
  26. console.log("age: ", this.age);
  27. }
  28. Sub.prototype.getName = function(){
  29. console.log("nameForSub: ", this.name);
  30. }
  31. function Sub1(name, age){
  32. Sup.call(this, name);
  33. this.age = age;
  34. }
  35. inherit(Sup, Sub1);
  36. Sub1.prototype.getAge = function(){
  37. console.log("age: ", this.age);
  38. }
  39. var a = new Sub("lilei", 25);
  40. a.items.push(4);
  41. a.getName();
  42. a.getAge();
  43. a.getItems();
  44. var b = new Sub("hanmeimei", 24);
  45. b.getName();
  46. b.getAge();
  47. b.getItems();
  48. var c = new Sub1("jim", 23);
  49. c.getName();
  50. c.getAge();
  51. c.getItems();
  52. //2.
  53. function inherit(Sup, Sub){
  54. Sub.prototype = Sup.prototype;
  55. }
  56. function Sup(name){
  57. this.name = name;
  58. this.items = [1, 2, 3];
  59. }
  60. Sup.prototype.getItems = function(){
  61. console.log("items: ", this.items);
  62. }
  63. Sup.prototype.getName = function(){
  64. console.log("name: ", this.name);
  65. }
  66. function Sub(name, age){
  67. Sup.call(this, name);
  68. this.age = age;
  69. }
  70. inherit(Sup, Sub);
  71. Sub.prototype.getAge = function(){
  72. console.log("age: ", this.age);
  73. }
  74. Sub.prototype.getName = function(){
  75. console.log("nameForSub: ", this.name);
  76. }
  77. function Sub1(name, age){
  78. Sup.call(this, name);
  79. this.age = age;
  80. }
  81. inherit(Sup, Sub1);
  82. Sub1.prototype.getAge = function(){
  83. console.log("age: ", this.age);
  84. }
  85. var a = new Sub("lilei", 25);
  86. a.items.push(4);
  87. a.getName();
  88. a.getAge();
  89. a.getItems();
  90. var b = new Sub("hanmeimei", 24);
  91. b.getName();
  92. b.getAge();
  93. b.getItems();
  94. var c = new Sub1("jim", 23);
  95. c.getName();
  96. c.getAge();
  97. c.getItems();
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注