继承?何为继承?
通俗易懂的理解: 你爸爸名下的财产以后都是你的。也就是说子类可以拥有父类的所有东西。并且子类可以在自己身上扩展方法。
那么?继承都有哪些呢?
1.原型链继承
2.经典继承(构造函数的继承)
3.组合继承(顾名思义:原型链继承和经典继承的结合)
4.寄生式继承
5.组合寄生式继承
首先我们谈谈原型链继承
子类的原型成为父类的实例
//创建一个父类构造函数
function Super(value) {
this.SuperValue = value
}
//给父类添加一个方法
Super.prototype.getSupervalue = function() {
return this.SuperValue
}
//创建一个子类构造函数
function Sub(value) {
this.Subvalue = value
}
//子类的原型去继承父类
Sub.prototype = new Super()
//给子类添加一个方法
Sub.prototype.getSubvalue = function() {
return this.Subvalue
}
const sub = new Sub(123)
console.log(sub.getSupervalue()); //undefined
console.log(sub.getSubvalue()); //123
这里要注意的是:子类添加方法一定要在子类继承父类以后,如果顺序反了,那么子类添加的所有原型方法都没用(因为子类的原型已经变成了父类的原型) 优点:
1.父类方法可以复用
缺点:
1.1. 父类的所有引用属性会被所有子类共享,更改一个子类的引用属性,其他子类也会受影响
下图代码执行结果
function Parent() {
this.isShow = true
this.info = {
name: "yhd",
age: 18,
};
}
Parent.prototype.getInfo = function() {
console.log(this.info);
console.log(this.isShow); // true
}
function Child() {};
Child.prototype = new Parent();
let Child1 = new Child();
Child1.info.gender = "男";
Child1.getInfo(); // {name: "yhd", age: 18, gender: "男"}
let child2 = new Child();
child2.getInfo(); // {name: "yhd", age: 18, gender: "男"}
child2.isShow = false
console.log(child2.isShow); // false
2.子类在创建实例的时候无法向父类构造函数传递参数
经典继承
在子类的构造函数中调用父类的构造函数,使用apply或call函数,更改父类构造函数的指向,让子类自己生成父类构造函数中定义的属性和方法
//创建父类构造函数
function Super() {
this.SuperArray = [1, 2, 3, 4]
this.k=function(){console.log('我是父类的方法')}
}
//创建子类构造函数并使用call或者apply改变this的指向
function Sub() {
Super.call(this)
}
const app = new Sub()
app.SuperArray.push(5)
console.log(app.SuperArray); //[1,2,3,4,5]
优点:
- 可以在子类构造函数中向父类传参数
- 父类的引用属性不会被共享
function Parent(name) {
this.info = { name: name };
}
function Child(name) {
//继承自Parent,并传参
Parent.call(this, name);
//实例属性
this.age = 18
}
let child1 = new Child("yhd");
console.log(child1.info.name); // "yhd"
console.log(child1.age); // 18
let child2 = new Child("wxb");
console.log(child2.info.name); // "wxb"
console.log(child2.age); // 18
缺点:
1.类的方法要在构造函数中声明,函数得不到复用
//创建父类构造函数
function Super() {
this.SuperArray = [1, 2, 3, 4]
this.k=function(){console.log('我是父类的方法')}
}
//创建子类构造函数并使用call或者apply改变this的指向
function Sub() {
Super.call(this)
}
const app = new Sub()
const app1=new Sub()
console.log(app.k==app1.k)//两个方法不是同一个方法(函数得不到服用) false
console.log(app1.k)
app.SuperArray.push(5)
console.log(app.SuperArray); //[1,2,3,4,5]
2.无法访问父类原型上的方法
//创建父类构造函数
function Super() {
this.SuperArray = [1, 2, 3, 4]
}
//给父类添加原型方法
Super.prototype.getSuper=function(){
console.log('我是父类原型上的方法')
}
//创建子类构造函数并使用call或者apply改变this的指向
function Sub() {
Super.call(this)
}
const app = new Sub()
app.getSuper() //这里会报错,这是父类原型上的方法,子类没有,所以报错
app.SuperArray.push(5)
console.log(app.SuperArray); //[1,2,3,4,5]
组合继承的实现
//父类构造函数
function Super(age) {
this.Superage = [12, 15, 14, 18]
this.age=age
}
//给父类构造函数的原型上添加方法
Super.prototype.sayage = function() {
return this.age
}
//子类构造函数,并将父类拿到子类里面去
function Sub(age,name) {
Super.call(this,age)
this.name=name
}
//子类的原型作为父类的实例
Sub.prototype = new Super()
Sub.prototype.sayname=function(){
return this.name
}
//子类构造器指向自己
Sub.prototype.construct = Sub
const app = new Sub(19,'zjx')
app.Superage.push(12)
console.log(app.Superage) //[12,15,14,18,12]
console.log(app.sayage()) //19
console.log(app.sayname()) //zjx
const app1 = new Sub(20,'kkk') //实现了向父类传值
console.log(app1.Superage) //[12,15,14,18]这里对比上面,实现了引用类型的改变不会影响其他的实列对象
console.log(app1.sayage()) //20
console.log(app1.sayname()) //kkk
优点:原型链继承和经典继承的优点他都有了 缺点:父类构造函数会被调用两次
寄生式继承的实现
核心:在原型式继承的基础上,增强对象,返回构造函数
function createAnother(original){
var clone=Object.create(original); // 通过调用 object() 函数创建一个新对象
clone.sayHi = function(){ // 以某种方式来增强对象
alert("hi");
};
return clone; // 返回这个对象
}
缺点:
1.无法实现函数复用
var person = {name:'zjx',arr:[1,3,2,5]};
var person1 = {name:'kkk',arr:[1,2,5]};
var anotherPerson = createAnother(person);
var anotherPerson1 = createAnother(person1);
anotherPerson.sayHi(); //"hi"
anotherPerson1.sayHi(); //"hi"
console.log(anotherPerson.sayHi==anotherPerson1.sayHi)//false(说明得函数不到复用)
寄生式组合继承实现
结合了以上所有继承的优点
//父类
function Super(age){
this.age=age
this.color=['green','red']
}
//添加父类原型上的方法
Super.prototype.getage=function (){
return this.age
}
//子类
function Sub(age,name){
Super.call(this,age)
this.name=name
}
//子类的原型作为父类的实例
Sub.prototype=Object.create(Super.prototype)
//子类的原型方法
Sub.prototype.getname=function(){
return this.name
}
// 修正子类原型的构造函数
Sub.prototype.constructor = Sub
let app=new Sub(12,'zjx') //子类的实例向父类传参
app.color.push('pink')
console.log(app.color) //green,red,pink
console.log(app.getage())//12 //说明可以调父类函数上的方法
console.log(app.getname())//zjx
let app1=new Sub(15,'kkk')
console.log(app1.color) //green,red 说明解决了子类实例改变父类属性的value值不会影响其他的实例对象