继承模式
先搭建好继承框架:
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()
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()
Sub.prototype.constructor = Sub
Sub.prototype.showSubPop = function (){
console.log(this.subProp)
}
var sub = new Sub()
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.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){
Person.call(this,name,age)
this.price = price
}
Student.prototype = new Person()
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){
this.name =name
this.age =age
this.price = price
}
Student.prototype = new Person()
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,参数) ,下面再将子类型的原型修改为父类型的实例的时候就无需传参了 。
|