一、js作为面向对象的若数据类型语言,其也是有继承特性的。但是由于js的类本身是函数,这就导致了js继承的特殊性。
- 既然要继承,那么我们就先创建一个父类,这个父类的构造函数里有一个属性和一个方法,原型上也有一个属性和一个方法。
//首先定义一个父类
function Parent(lastName = '建国'){
this.lastName = lastName
//一个方法
this.canDo = () => {
console.log(`${this.firstName + this.lastName} can sleeping and eating only`)
}
}
//一个原型上的方法
Parent.prototype.firstName = '张'
Parent.prototype.say = function () {
console.log(`我名字就是${this.lastName}`)
}
let p = new Parent()
- 父类的方法有了,那我们怎么让子类继承父类呢?
2.1 原型链继承
原型链继承不能继承父类构造函数上的属性和方法,且如果是引用的方式,修改子类的原型会影响到父类
//让Son原型 为Parent的实例
// Son.prototype = new Parent()
//让Son的原型 引用 Parent的原型
Son.prototype = Parent.prototype
function Son(lastName){
this.lastName = lastName
}
let s = new Son('小红')
2.2 构造函数继承
构造函数继承并不能继承父类原型上的方法和属性
function Son(lastName){
Parent.call(this,lastName)
}
let s = new Son('小红')
2.3 另有实例继承、拷贝继承等方式,都不常用
2.4 组合继承,由于子类的原型是父类实例,导致子类原型被覆盖
//组合继承
function Son(lastName){
Parent.call(this,lastName)
}
//子类原型上的cry方法就不可调用
Son.prototype.cry = function (){
console.log('con cry')
}
Son.prototype = new Parent()
let s = new Son('小红')
2.5 组合寄生继承,继承常用的方法,主要思路是利用子类的构造器,和没有实例属性的父类为原型,弥补了之前各种方法的不足。
function Son(lastName){
Parent.call(this,lastName)
}
(function (){
//创建一个没有方法属性的函数,用于过度
let F = function(){}
F.prototype = Parent.prototype
Son.prototype = new F()
})()
//子类原型上的方法也能够使用了
Son.prototype.cry = function () {
console.log('son cry')
}
let s = new Son('小红')
二、根据上面的描述,整体写一下继承的函数
- 封装一个简单的继承函数
let inherit = (function (){
let F = function (){}
return function(Target, Origin){
F.prototype = Origin.prototype
Target.prototype = new F()
Target.prototype.constructor = Target
Target.prototype.uber = Origin.prototype
}
})()
inherit(Son, Parent)
2.改进后完整的例子,简单的总结继承就是用子类自己的构造器,用父类的原型
//父类
function Parent(lastName = '建国'){
this.lastName = lastName
this.canDo = () => {
console.log(`${this.firstName + this.lastName} can sleeping and eating only`)
}
}
//父类原型上的方法
Parent.prototype.firstName = '张'
Parent.prototype.say = function () {
console.log(`我名字就是${this.lastName}`)
}
//子类
function Son(lastName,nickname){
Parent.call(this,lastName)
this.nickname = '红红'
}
function inherit(Target, Origin){
//这里可以直接得到之前的空实例原型
let proto = Object.create(Origin.prototype)
proto.constructor = Target
console.log(proto)
Target.prototype = proto
}
inherit(Son, Parent)
//子类原型上的方法也能够使用了
Son.prototype.cry = function () {
console.log('son cry')
}
let s = new Son('小红')
三、在es6里,继承的实现可以变的更加简单
class Father{
constructor(lastName='建国'){
this.lastName = lastName
this.firstName = '张'
}
canDo(){
console.log(`${this.firstName + this.lastName} can do nothing`)
}
}
class Son extends Father{
constructor(lastName){
super()
this.lastName = lastName
}
cry(){
console.log(`${this.lastName} just can cry`)
}
canDo(){
//方法也能继承,如果不写super,就会覆盖父类方法,类似于c++重写
super.canDo();
console.log(`${this.lastName} 厉害了,什么都能做`)
}
}
let s = new Son('红红')