6 JavaScript inheritance methods and their advantages and disadvantages (summary)

The first type: prototype chain inheritance

Utilize the characteristics of the prototype chain for inheritance

1

2

3

4

5

6

7

8

9

10

11

function Parent(){

  this.name = 'web前端';

  this.type = ['JS','HTML','CSS'];

}

Parent.prototype.Say=function(){

  console.log(this.name);

}

function Son(){};

Son.prototype = new Parent();

son1 = new Son();

son1.Say();

The above example explains:

①Create a constructor called Parent, temporarily called the parent constructor, which has two attributes name and type
②Set the Say method through the attributes of the Parent constructor (i.e. the prototype object). At this time, Parent has 2 attributes and 1 Method
③Create a constructor called Son, temporarily called the child constructor
④Set the property (ie prototype object) value of Son to the instance object of the parent constructor Parent, that is, the child constructor Son inherits the parent constructor Parent, this Son also has 2 attributes and 1 method.
⑤ Instantiate the Son constructor, and assign the result to the variable son1, that is, son1 is the instantiated object. It also has 2 attributes and 1 method. ⑥
Output the Say method of son1, and the result is "web front end"

Advantages: Inheritance can be achieved

Disadvantages:
① Because Son.prototype (i.e. prototype object) inherits the Parent instantiated object, this results in all Son instantiated objects being the same and sharing the properties and methods of the prototype object. code show as below:

1

2

3

4

5

son1 = new Son();

son2 = new Son();

son1.type.push('VUE');

console.log(son1.type);//['JS','HTML','CSS','VUE']

console.log(son2.type);//['JS','HTML','CSS','VUE']

As a result, son1 and son2 are both ['JS', 'HTML', 'CSS', 'VUE']
② The Son constructor instantiated object cannot pass parameters

Second type: constructor inheritance

Inherit through the constructor call method and look at the code directly:

1

2

3

4

5

6

7

8

9

10

11

12

function Parent(){

  this.name = 'web前端';

  this.type = ['JS','HTML','CSS'];

}

function Son(){

  Parent.call(this);

}

son1 = new Son();

son1.type.push('VUE');

console.log(son1.type);//['JS','HTML','CSS','VUE']

son2 = new Son();

console.log(son2.type);//['JS','HTML','CSS']

以上例子解释:
①创建父级构造函数Parent,有name、type两个属性
②创建子级构造函数Son,函数内部通过call方法调用父级构造函数Parent,实现继承
③分别创建构造函数Son的两个实例化对象son1、son2,对son1的type属性新增元素,son2没有新增,结果不一样,说明实现了独立

优点:
①实现实例化对象的独立性;
②还可以给实例化对象添加参数

1

2

3

4

5

6

7

8

9

10

function Parent(name){

  this.name = name;

}

function Son(name){

  Parent.call(this,name);

}

son1 = new Son('JS');

console.log(son1);//JS

son2 = new Son('HTML');

console.log(son2);//HTML

缺点:
①方法都在构造函数中定义,每次实例化对象都得创建一遍方法,基本无法实现函数复用
②call方法仅仅调用了父级构造函数的属性及方法,没有办法调用父级构造函数原型对象的方法

第三种:组合继承

利用原型链继承和构造函数继承的各自优势进行组合使用,还是看代码:

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.type = ['JS','HTML','CSS'];

}

Parent.prototype.Say=function(){

  console.log(this.name);

}

function Son(name){

  Parent.call(this,name);

}

Son.prototype = new Parent();

son1 = new Son('张三');

son2 = new Son('李四');

son1.type.push('VUE');

son2.type.push('PHP');

console.log(son1.type);//['JS','HTML','CSS','VUE']

console.log(son2.type);//['JS','HTML','CSS','PHP']

son1.Say();//张三

son2.Say();//李四

以上例子解释:
①创建一个叫做Parent的构造函数,里面有两个属性name、type
②通过Parent构造函数的属性(即原型对象)设置Say方法,此时,Parent有2个属性和1个方法
③创建子级构造函数Son,函数内部通过call方法调用父级构造函数Parent,实现继承
④子构造函数Son继承了父构造函数Parent,此时Son也有2个属性和1个方法
⑤分别创建构造函数Son的两个实例化对象son1、son2,传不同参数、给type属性新增不同元素、调用原型对象Say方法

优点:
①利用原型链继承,实现原型对象方法的继承
②利用构造函数继承,实现属性的继承,而且可以参数
组合函数基本满足了JS的继承,比较常用

缺点:
无论什么情况下,都会调用两次父级构造函数:一次是在创建子级原型的时候,另一次是在子级构造函数内部

第四种:原型式继承

创建一个函数,将参数作为一个对象的原型对象

1

2

3

4

5

6

7

8

9

10

11

12

function fun(obj) {

  function Son(){};

  Son.prototype = obj;

  return new Son();

}   

var parent = {

  name:'张三'

}

var son1 = fun(parent);

var son2 = fun(parent);

console.log(son1.name);//张三

console.log(son2.name);//张三

以上例子解释:
①创建一个函数fun,内部定义一个构造函数Son
②将Son的原型对象设置为参数,参数是一个对象,完成继承
③将Son实例化后返回,即返回的是一个实例化对象
优缺点:跟原型链类似

第五种:寄生继承

在原型式继承的基础上,在函数内部丰富对象

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

function fun(obj) {

  function Son() { };

  Son.prototype = obj;

  return new Son();

}

function JiSheng(obj) {

  var clone = fun(obj);

  clone.Say = function () {

    console.log('我是新增的方法');

  }

  return clone;

}

var parent = {

  name: '张三'

}

var parent1 = JiSheng(parent);

var parent2 = JiSheng(parent);

console.log(parent2.Say==parent1.Say);// false

以上例子解释:
①再原型式继承的基础上,封装一个JiSheng函数
②将fun函数返回的对象进行增强,新增Say方法,最后返回
③调用JiSheng函数两次,分别赋值给变量parent1、parent2
④对比parent1、parent2,结果为false,实现独立
优缺点:跟构造函数继承类似,调用一次函数就得创建一遍方法,无法实现函数复用,效率较低

这里补充一个知识点,ES5有一个新的方法Object.create(),这个方法相当于封装了原型式继承。这个方法可以接收两个参数:第一个是新对象的原型对象(可选的),第二个是新对象新增属性,所以上面代码还可以这样:

1

2

3

4

5

6

7

8

9

10

11

12

13

function JiSheng(obj) {

  var clone = Object.create(obj);

  clone.Say = function () {

    console.log('我是新增的方法');

  }

  return clone;

}

var parent = {

  name: '张三'

}

var parent1 = JiSheng(parent);

var parent2 = JiSheng(parent);

console.log(parent2.Say==parent1.Say);// false

第六种:寄生组合继承

利用组合继承和寄生继承各自优势
组合继承方法我们已经说了,它的缺点是两次调用父级构造函数,一次是在创建子级原型的时候,另一次是在子级构造函数内部,那么我们只需要优化这个问题就行了,即减少一次调用父级构造函数,正好利用寄生继承的特性,继承父级构造函数的原型来创建子级原型。

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 JiSheng(son,parent) {

  var clone = Object.create(parent.prototype);//创建对象

  son.prototype = clone;   //指定对象

  clone.constructor = son;   //增强对象

}

function Parent(name){

  this.name = name;

  this.type = ['JS','HTML','CSS'];

}

Parent.prototype.Say=function(){

  console.log(this.name);

}

function Son(name){

  Parent.call(this,name);

}

JiSheng(Son,Parent);

son1 = new Son('张三');

son2 = new Son('李四');

son1.type.push('VUE');

son2.type.push('PHP');

console.log(son1.type);//['JS','HTML','CSS','VUE']

console.log(son2.type);//['JS','HTML','CSS','PHP']

son1.Say();//张三

son2.Say();//李四

以上例子解释:
①封装一个函数JiSheng,两个参数,参数1为子级构造函数,参数2为父级构造函数
②利用Object.create(),将父级构造函数原型克隆为副本clone
③将该副本作为子级构造函数的原型
④给该副本添加constructor属性,因为③中修改原型导致副本失去默认的属性

优缺点:
组合继承优点、寄生继承的有点,目前JS继承中使用的都是这个继承方法

Guess you like

Origin blog.csdn.net/ljy_1024/article/details/119948663