继承模式
先搭建好继承框架:
// 父类型
function Supper (){
this.supProp = 'Supper property'
}
Supper.prototype.showSupperPop = function (){
console.log(this.supProp)
}
// 子类型
function Sub (){
this.subProp = 'Sub property'
}
Sub.prototype.showSubPop = function (){
console.log(this.subProp)
}
var sub = new Sub()
sub.showSupperPop()//想要实现调用,当然现在这样写是错误的
原型链继承
首先思考如何能够看到 Supper的showSupperPop方法
- 利用原型链
如果子类型的原型对象成为父类型的一个实例对象
,子类型就可以通过原型链看到父类型的属性和方法了,即实现了继承。
对应代码:Sub.prototype = new Supper()
解释:
上面代码的原型链:
如果想要sub实例可以访问Supper原型对象的showSupperPop(),就需要有一个箭头从 sub原型对象的__proto__
指向 Supper的原型对象
。即,使子类型的原型对象成为父类型的一个实例对象。
可能会问为什么不直接 从 sub实例对象的 __proto__
指向 Supper的原型对象
,如果这样的话,子类型的属性和方法就访问不到了。
注意,一个对象的 proto 只能存一个值。
(这里先这样理解,实际上真正实现继承不单单只是将sub原型对象的__proto__
的指向改变了,可以看下面实际操作一步步画出的原型链进行理解。)
实现:
将子类型的原型对象定义为父类型的实例对象
// 父类型
function Supper (){
this.supProp = 'Supper property'
}
Supper.prototype.showSupperPop = function (){
console.log(this.supProp)
}
// 子类型
function Sub (){
this.subProp = 'Sub property'
}
// 子类型的原型成为夫类型的实例对象
Sub.prototype = new Supper()
Sub.prototype.showSubPop = function (){
console.log(this.subProp)
}
var sub = new Sub()
sub.showSupperPop()
sub.showSubPop()
console.log(sub.toString())
输出:
此时对应的原型链:
问题
问题产生
constructor属性返回对象的构造函数。
那么上面的代码如果执行console.log(sub.constructor)
,会是什么结果呢
// 父类型
function Supper (){
this.supProp = 'Supper property'
}
Supper.prototype.showSupperPop = function (){
console.log(this.supProp)
}
// 子类型
function Sub (){
this.subProp = 'Sub property'
}
// 子类型的原型成为夫类型的实例对象
Sub.prototype = new Supper()
Sub.prototype.showSubPop = function (){
console.log(this.subProp)
}
var sub = new Sub()
// constructor属性返回对象的构造函数
console.log(sub.constructor)
输出:
输出的是Supper而不是我们预想的Sub,这是什么原因呢?
还是看刚才的原型链:sub实例对象没有constructor属性,就会像上找找到Supper的实例对象,发现也没有该属性,继续上找,找到Supper的原型对象发现有该属性,并且指向Supper的函数对象,所以输出就是Supper的函数对象。
问题解决
我们一般让对象的constructor指向自己的原型对象,所以做如下修改:
// 让子类型原型对象的constructor属性指向Sub
Sub.prototype.constructor = Sub
代码:
// 父类型
function Supper (){
this.supProp = 'Supper property'
}
Supper.prototype.showSupperPop = function (){
console.log(this.supProp)
}
// 子类型
function Sub (){
this.subProp = 'Sub property'
}
// 子类型的原型成为夫类型的实例对象
Sub.prototype = new Supper()
// 让子类型原型对象的constructor属性指向Sub
Sub.prototype.constructor = Sub
Sub.prototype.showSubPop = function (){
console.log(this.subProp)
}
var sub = new Sub()
// constructor属性返回对象的构造函数
console.log(sub.constructor)
输出:
对应的原型链:
借用构造函数继承——不是真正的继承
方法:
1.定义父类型构造函数
2.定义子类型构造函数
3.在子类型构造函数中调用父类型构造,利用函数的call
方法
call方法的作用就是短暂的调用一个不属于自己的函数。
eg:
function Person (name ,age){
this.name = name
this.age =age
}
function Student (name,age,price){
Person.call(this,name,age)
/*
含义相当于 this.Person(name,age)
即:
this.name = name
this.age =age
(注意this.Person(name,age)这句话不能执行,this没有Person属性,这样写只是为了方便理解)
*/
this.price = price
}
var s =new Student('Tom',12,10000)
console.log(s.name,s.age,s.price)
输出:
组合继承
组合继承就是将原型链继承和借用构造函数继承结合起来。
- 利用原型链实现对父类型对象的方法继承
- 利用call()借用父类型构造函数初始化相同属性
eg:
function Person (name ,age){
this.name = name
this.age =age
}
Person.prototype.setName=function(name){
this.name = name
}
function Student (name,age,price){
// 借用父类型构造函数初始化相同属性
//使用call,下面Student.prototype = new Person()就无需传参了
Person.call(this,name,age)
this.price = price
}
// 继承原型链,继承父类型对象的方法继承
//因为上面使用Person.call(this,name,age),这里就无需传参了
Student.prototype = new Person()
// 修正constructor属性
Student.prototype.constructor =Student
Student.prototype.setPrice=function(price){
this.setPrice = price
}
var s = new Student('Tom',24,10000)
s.setPrice(16000)
s.setName('Bob')
console.log(s.name,s.age,s.price)
输出:
我们通常使用的是组合继承
- 为什么不使用原型链继承?
因为夫类型的构造函数一般是要传递参数的,而我们使用Sub.prototype = new Supper()
只是为了建立原型链的关系,一般不需要传参,就出现了矛盾。
如果执意不传参,就会出现如下错误:
function Person (name ,age){
this.name = name
this.age =age
}
Person.prototype.setName=function(name){
this.name = name
}
function Student (name,age,price){
// 借用父类型构造函数初始化相同属性
// Person.call(this,name,age)
this.name =name
this.age =age
this.price = price
}
// 继承原型链,继承父类型对象的方法继承
Student.prototype = new Person()
// 修正constructor属性
Student.prototype.constructor =Student
Student.prototype.setPrice=function(price){
this.setPrice = price
}
var s = new Student('Tom',24,10000)
s.setName('Bob')
s.setPrice(16000)
console.log(s.name,s.age,s.price)
输出:
- 为什么不使用借用构造函数继承?
因为他只是表面上的简单引用,原型链上的方法属性不会继承。
所以使用两种方式相结合的方法实现组合继承:
在子类型的构造函数中使用 父构造函数.call(this,参数)
,下面再将子类型的原型修改为父类型的实例的时候就无需传参了 。