js 继承

一、原型链
//父类声明
function Parent(){
    this.name = 'papa'
}
//为父类添加共有方法
Parent.prototype.greet = function(str){
    console.log("parent say: ",str)
}
//声明子类
function Child(){
    this.name = 'child'
}
//继承父类
Child.prototype = new Parent()

//验证继承是否成功
console.log(Child.prototype)
let xiaoming = new Child()
xiaoming.greet("hello, I am xiaoming")

继承结果:
在这里插入图片描述
注意事项:
这样的继承方式,实现的实质是以Parent的一个实例来重写Child的prototype属性。
引用类型的原型属性被所有的继承者共享,如果在某个继承者的实例上修改了原型的属性,那么所有的继承者继承的属性都会被修改:
在这里插入图片描述

二、借用构造函数(伪造对象、经典继承)
//Parent declaration
function Parent(name){
    this.name = name || 'hua'
    this.parent = ['nainai','yeye']
}
//为父类添加共有方法
Parent.prototype.greet = function(str){
    console.log("parent say: ",str)
}
//声明子类时实现继承
function Child(name){
    Parent.apply(this,arguments)
}

//是否继承
let xiaoMing = new Child('xiaoming')
console.log(xiaoMing)

继承结果:
在这里插入图片描述
注意事项:
通过借用构造函数来实现继承,无法继承Parent构造函数之外定义的方法,对比上面两图,第一种可以继承greet方法,第二种没有继承。

三、组合继承

组合继承其实就是将原型链继承和借用构造函数继承组合在一起。

实现思路: 使用原型链实现对原型属性和方法的继承,通过借用构造函数实现对实例属性的继承。

优点: 通过在原型上定义方法实现了函数复用,同时能够保证每个实例有自己的属性,避免了原型链和借用构造函数的缺陷。此时引用类型属性(比如parent)其实是通过apply复制到子类中的,所以不会发生共享。
缺点: 它调用了两次父类的构造函数。

//Parent declaration
function Parent(name){
    this.name = name || 'hua'
    this.parent = ['nainai','yeye']
}
//为父类添加共有方法
Parent.prototype.greet = function(str){
    console.log("parent say: ",str)
}
//声明子类时借用构造函数
function Child(name){
    Parent.apply(this,arguments)
}
//重写prototype
Child.prototype = new Parent()

//是否继承
let xiaoMing = new Child('xiaoming')
xiaoMing.parent.push('mingPa','mingMa')

let Hong = new Child('hong')
console.log(Hong.parent)

继承结果:
在这里插入图片描述

四、寄生组合式继承

寄生组合式继承优化的部分:在组合继承的基础上减少一次多余的调用父类的构造函数。

//Parent declaration
function Parent(name){
    this.name = name || 'hua'
    this.parent = ['nainai','yeye']
}
//为父类添加共有方法
Parent.prototype.greet = function(str){
    console.log("parent say: ",str)
}
//声明子类时借用构造函数
function Child(name){
    Parent.apply(this,arguments)
}
//划重点!!!
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child

//是否继承
let xiaoMing = new Child('xiaoming')
xiaoMing.parent.push('mingPa','mingMa')

let Hong = new Child('hong')
console.log("xiao ming's parents: ",xiaoMing.parent)
console.log('hong\'s parents: ',Hong.parent)

划重点的两部分:

  1. 第一行:没有像原型链继承一样直接将父类Parent的一个实例赋值给Child.prototype,而是使用Object.create()进行一次浅拷贝,将父类原型上的方法拷贝后赋给Child.prototype,这样子类上就能拥有了父类的共有方法,而且少了一次调用父类的构造函数。
    Object.create()的浅拷贝的作用类似下面的函数:
function create(obj) {

  function F() {};

  F.prototype = obj;

  return new F();

}

第二行:
因为对Parent的原型进行了拷贝后赋给Child.prototype,因此Child.prototype上的constructor属性也被重写了,所以要修复这一个问题。

继承结果:
在这里插入图片描述

五、ES6 —— 通过extends关键字实现继承

class Parent {
    constructor(name){
        this.name = name
    }
    greet(str){
        console.log('hello, ',str)
    }
}

class Child extends Parent {
    constructor(name){
        super(name)
        this.name = name
    }
}
let ming = new Child('ming')
ming.greet('world')
console.log(ming.name)

注意事项:

  1. 子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。所以只有调用super之后,才可以使用this关键字,否则会报错。子类实例的构建,基于父类实例,只有super方法才能调用父类实例。
  2. 如果子类没有定义constructor方法,这个方法会被默认添加,代码如下。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。
class ColorPoint extends Point {
}

// 等同于
class ColorPoint extends Point {
  constructor(...args) {
    super(...args);
  }
}

继承结果:
在这里插入图片描述
PS:
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值