JavaScript:详解JS继承

JavaScript:详解JS继承

方法一:原型链继承

  • 核心:将父类实例作为子类原型
  • 优点:方法复用
  • 缺点: 1.创建子类实例的时候,不能传父类的参数 2.子类实例共享了父类构造函数的引用属性 3.无法实现多继承
function Parent(name){
    this.name = name || '父亲'; //实例基本属性
    this.arr=[1];//引用数据类型
}
Parent.prototype.say=function(){
    console.log('hello')
}
function Child(like){
    this.like=like;
}
Child.prototype=new Parent();//核心,但此时Child.prototype.constructor==Parent
Child.prototype.constructor=Child;

let boy1 = new Child();
let boy2 = new Child();
//优点:共享了父类构造函数的say方法
console.log(boy1.say(),boy2.say(),boy1.say===boy2.say);//hello hello true
//缺点1:不能向父类构造函数传参
console.log(boy1.name,boy2.name,boy1.name===boy2.name);//父亲,父亲,true
//缺点2:子类实例共享了父类构造函数的引用数据类型
boy1.arr.push(2);
//修改了boy1的arr属性,boy2的arr属性也会改变,因为两个实例的原型上有arr
console.log(boy2.arr);//[1,2]

注意1:修改boy1的name属性,是不会影响到boy2.name。因为设置boy1.name相当于在子类实例新增了name属性
复制代码

方法二:借用构造函数

  • 核心:接用父类的构造函数来强化子类实例,等于是复制父类的实例属性给子类
  • 优点:实例之间独立
  1. 创建子类实例,可以向父类构造函数传参数。
    复制代码
  2. 子类实例不共享父类构造函数的引用属性。
    复制代码
  3. 可实现多继承(通过多个call或者apply继承多个类)
    复制代码
  • 缺点:
  1. 父类的方法不能复用
  2. 子类实例,继承不了父类原型上的属性
function Parent(name){
    this.name=name;//实例基本属性
    this.arr=[1];
    this.say=function(){
        console.log('hello')
    }
}
function Child(name,like){
    Parent.call(this.name);//核心 拷贝了父类的实例属性和方法
    this.like=like;
}
let boy1=new Child('小红','apple')
let boy2=new Child('小明''orange')
//优点1:可向父类构造函数传参
console.log(boy1.name,boy2.name);//小红  小明
//优点2:不共享父类构造函数的引用属性
boy1.arr.push(2);
console.log(boy1.arr,boy2.arr);//[1,2][1]
//缺点1:方法不能复用
console.log(boy1.say===boy2.say);//false(boy1和boy2的say方法是独立,不是共享的)
//缺点2:不能继承父类原型上的方法
Parent.prototype.walk=function(){
    console.log('我会走路')
}
boy1.walk;//undefined
复制代码

方法三:组合继承

  • 核心:通过调用父类构造函数,继承父类的属性并保留传参的优点;然后通过将父类实例作为子类原型,实现函数复用。
  • 优点:保留了构造函数与原型链继承的优点
  • 缺点:由于调用了两次父类的构造函数,会存在一份多余的父类实例属性
  • 注意:‘组合继承’这个方式,要记得修复Child.prototype.constructor指向
function Parent(name){
    this.name=name;//实例基本属性
    this.arr=[1];//引用属性
}
Parent.prototype.say=function(){
    console.log('hello')
}
function Child(name,like){
    Parent.call(this,name,like)//核心第二次
    this.like=like;
}
Child.prototype=new Parent()//核心第一次
Child.prototype.constructor=Child//修正constructor指向

let boy1=new Child('小红','apple')
let boy2=new Child('小明','orange')
复制代码

其实Child.prototype=new Parent()
console.log(Child.prototype.protp===Parent.prototype);
因为Child.prototype等于Parent的实例,所以__proto__指向Parent.prototype

方法四:组合继承优化1

  • 核心:通过这个方式,砍掉父类的实例属性,这样在调用父类构造函数的时候,就不会初始化两次实例,避免了组合继承的缺点
  • 优点:
  1. 只调用了一次父类构造函数。
  2. 保留构造函数与原型链的优点
  • 缺点:修正构造函数指向之后,父类实例的构造函数指向,同时也发生了变化。
  • 注意:‘组合继承优化1’这种方式,要记得修复Child.prototype.constructor指向

原因是:不能判断子类实例的直接构造函数,到底是子类构造函数还是父类构造函数

function Parent(name){
    this.name=name;//实例基本属性
    this.arr=[1];//引用属性
}
Parent.prototype.say=function(){
    console.log('hello')
}
function Child(name,like){
    Parent.call(this,name,like)//核心
    this.like=like;
}
Child.prototype=Parent.prototype//核心 子类原型和父类原型,实质上是同一个

//缺点1:当修复子类构造函数的指向后,父类实例的构造函数指向也会跟着变了
没修复之前:console.log(boy1.constructor);//Parent
修复代码:Child.prototype.constructor=Child
修复之后:console.log(boy1.constructor);//Child
console.log(p1.constructor);//Child(希望是parent)
原因:因为是通过原型来实现继承的,Child.prototype的上面是没有construtor属性的,就会往上找,
这样就找到了Parent.prototype上面的construtor属性;当你修改了子类实例的constructor属性,
所有的constructor的指向都会发生变化
复制代码

方法五:寄生组合继承

  • 优点:完美
function Parent(name){
    this.name=name;
    this.arr=[1];
}
Parent.prototype.say=function(){
    console.log('hello')
}
function Child(name,like){
    Parent.call(this,name,like)//核心
    this.like=like;
}
//核心:通过创建中间对象,子类原型和父类原型就会隔离开,避免了方法4的缺点
Child.prototype=Object.create(Parent,prototype)
//这里是修改构造函数指向的代码
Child.prototype.constructor=Child
复制代码

方法流:ES6 Class类实现继承

class Person{
    constructor(name){
        this.name=name;
    }
    drink(){
        console.log('喝水');
    }
}
class Student extends Person{
    constructor(name,score){
        super(name);//super关键字执行父类的构造函数
        this.score=score;
    }
    introduce(){
        console.log(`我是${this.name},考了${this.score}分`)
    }
}
const student=new Student('张三'99);
console.log('student',student);
student.drink();
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享