JavaScript 之prototype与继承

prototype

  每个构造函数都有一个prototype属性,这个属性就是这个构造函数的原型对象。构造函数实例所共享的属性和方法都存在这个原型对象上。


继承
  1. 原型链继承
    基本原理就是让构造函数A的原型对象等于另一个构造函数B的实例,那么A便继承了B
    function Parent() {
        this.role = 'parent'
    }
    
    function Children() {
        this.role = 'children'
    }
    
    Children.prototype = new Parent()
    
    原型链继承有两个问题
    • 原型中包含引用类型值的问题
          function Parent() {
              this.colors = ['red', 'blue']
          }
          
          function Children() {}
          
          Children.prototype = new Parent()
          
          const children = new Children()
          const childrenCopy = new Children()
          
          children.colors.push('black')
          console.log(childrenCopy.colors) // ['red', 'blue', 'black']
          ```
      
    • 创建子类型实例的时候不能向父构造函数传递参数

  2. 借用构造函数 | 伪造对象 | 经典继承
    在子类型内部调用父类型函数。就是将所有共享的属性和方法都定义在父类型中,通过call()apply()方法在子类型中执行父类型函数,其本质是将父类型中定义的方法和属性复制一份到子类型中
    function Parent(name, sex) {
        this.name = name
        this.sex = sex
        this.getSex = function() {
            console.log(this.sex)
        }
    }
    
    function Children(name,sex) {
        Parent.call(this, name, sex)
    }
    
    const children = new Children('小吴', '男')
    
    借用构造函数虽然解决了原型链继承的传参问题和引用类型值的问题,但是它也造成了其它两个问题
    • 当父类型中定义方法过多时,会占用很大内存,这样方法复用就无从谈起了,而且父类型的原型对子类型是不可见的
    • 当要修改父类型中的一个属性或者方法时,那么修改之前的所有实例都不能及时作出更新

  3. 组合继承 | 伪经典继承
    使用原型链来实现对父类型原型属性和方法的继承,使用借用构造函数实现父类型属性的继承
    function Parent(name) {
        this.name = name
    }
    
    Parent.prototype.getName = function() {
        console.log(this.name)
    }
    
    function Children(name) {
        Parent.call(this, name)
    }
    
    Children.prototype = new Parent()
    
    const children = new Children('小吴')
    
    组合继承避免了原型链继承和借用构造函数继承的缺点,融合了它们的优点,成为了js中最常用的继承模式。但是组合继承也存在一个缺点
    • 会调用两次父类型构造函数,造成性能上的浪费

  4. 原型式继承
    原型式继承的inherit方法本质上是对参数对象的一个浅复制
    其问题也和原型链继承相同,都是引用类型值的问题以及向父类型传参的问题
    function inherit(Parent) {
        const Fn = function() {}
        Fn.prototype = Parent
        return new Fn()
    }
    
    const Parent = {
        name: '老吴',
        getName() {
            console.log(this.name)
        }
    }
    
    const children = inherit(Parent)
    

  5. 寄生式继承
    这种继承并没有什么优点,只是提供一种思路而已
    function inherit(Parent) {
        const Fn = function() {}
        Fn.prototype = Parent
        return new Fn()
    }
    
    function createChildren(Parent) {
        const children = inherit(Parent)
        children.sayHi = funtion() {
            console.log('Hi')
        }
    }
    
    const Parent = {
        name: '老吴',
        getName() {
            console.log(this.name)
        }
    }
    
    const children = creatChildren(Parent)
    

  6. 寄生组合式继承
    这是一种最完美的继承方式
    function getCopyObj(obj) {
        const Fn = function() {}
        Fn.prototype = obj
        return new Fn()
    }
    
    function inherit(Children, Parent) {
        const parentPrototype = getCopyObj(Parent.prototype)
        parentPrototype.constructor = Children
        Children.prototype = parentPrototype
    }
    
    function Parent(name) {
        this.name = name
    }
    
    Parent.prototype.getName() {
        console.log(this.name)
    }
    
    function Children(name) {
        Parent.call(this, name)
    }
    
    inherit(Children, Parent)
    
    const children = new Children('小吴')
    
    寄生组合式继承主要是为了解决组合式继承中两次调用父类型构造函数的问题的。从代码上来看,使用了getCopyObj方法和inherit方法来代替组合式继承中子类型构造函数等于父类型构造函数实例(Children.prototype = new Parent()),起原理是拷贝父类型构造函数的原型对象,并且修改其constructor指针位置,然后再赋值给子类型构造函数的原型对象
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值