在js面向对象的集成中有三种集成,分别为构造函数继承、原型继承、组合继承
一、构造函数继承
首先我们创建一个构造函数,函数中分别有型参name,age,代码片段如下图所示:
在构造函数的原型中添加一个方法,代码块如下图所示:
创建完毕构造函数之后,我们在创建另外一个构造函数 function Student(name,age,grade),我们需要让第二个构造函数继承第一个构造函数中的say()这个方法,如果我们在第二个构造函数中同样的写
this.name = name ;
this.age = age ;
this.grade = grade
就相当于没有继承Preson构造函数中的属性,相当于Student构造函数自己又重新写了一遍,这样的写法有点浪费。详情代码块如下图所示:
为了解决上面问题,我们该如何将Person构造函数中的属性给继承过来呢?
学习过js的小伙伴都知道,在js中有三个方法可以改变this的指向,分别为call(),apply(),bind()
call()的作用是调用函数,同时改变this的指向,同时也可以继承,可以将父类构造函数中的一些属性继承到子类中。
apply() 调用一个函数,简单的理解为调用函数的方式,但是ta可以改变函数的this指向。
function.apply(thisArg,[argsArray])
thisArg:为function函数运行时指定的this值。
argsArray:传递的值必须包含在数组里面
返回值就是函数的返回值,因为ta就是调用函数。
bind()方法不会调用函数,但是能改变内部的this指向,语法格式为
function.bind(thisArg,arg1,arg2,.....)
thisArg:为function函数运行时指定的this值。
arg1,arg2,....:传递的其他参数。
call(),apply(),bind()总结
call()经常做一些继承,使子构造函数继承父构造函数
apply() 经常跟数组有关系,比如借助于数学对象实现数组最大最小值。
bind() 不调用函数,但是还想改变this指向,比如改变定时器内部的this指向。
回归到面向对象的继承当中,我们就使用call这个方法,
我们将子构造函数中使用call(),代码块如下所示:
在上面代码块中,实例化子构造函数之后,打印的结果为
上面的代码表示的是,通过call方法,将父构造函数的this强行改变为成Student子构造函数中的this,将this挂载上grade属性,同时又挂载上name属性及grade属性。这样就将父构造函数中的属性继承过来了,但是并没有继承父构造函数中的方法。
我们该如何继承父类构造函数中的方法呢?有的小伙伴可能会想到,将父构造函数中的原型上的方法赋值给子构造函数的原型上,这样想也没什么问题,下面代码演示一下。
打印结果,发现在子构造函数的原型中确实有say()这个方法。
此时我们给子构造函数的原型中添加自己的方法,看看会有什么变化。
我们实例化父类构造函数,打印
我们发现,父类构造函数中的原型中也有子类构造函数的方法,这是不可以的。 上面的代码中将父构造函数原型赋值给子构造函数原型,就会造成,父构造函数的原型与子构造函数原型一模一样的了,如果用户更改父构造函数原型就会影响到子构造函数原型,更改子构造函数原型也会影响到父构造函数原型,因为这俩个原型是复杂数据类型只是一个引用。
代码上述写会互相影响,并没有实现方法的继承。
那么我们该如何继承到父类构造函数中的方法呢?
解决方案就是实例化父类,将实例化后的父类赋值给子类的原型中,(一旦new Person)new完父类对象后,该对象中就会有属性和方法,详情代码块如下图所示:
此时子类构造函数在调用父类中的方法则可以调用的
我们在打印子类构造函数,发现子构造函数的原型上是new Person(),而且多了name和age属性,也就是我们在上述代码中new Person()没有传递参数,相当于Person构造函数中的
this.name = undefined
this.age = undefined
而最终say方法就被继承过来了。如果有小伙伴担心,我们将来更改name属性与age方法会不会受到影响,完全不会的,我们前面学习过原型链,构造函数new之后,首先会在自己身上找属性,如果没有才会去原型链上去找的。
解释那么多,请看下面打印界面。