zoukankan      html  css  js  c++  java
  • JavaScript学习笔记(十四) 继承

    这篇文章将会介绍在 JavaScript 中经常使用的六种继承方式

    1.1 原型继承

    方法:将子类的原型指向父类的实例

    原理:子类在访问属性或调用方法时,往上查找原型链,能够找到父类的属性和方法

    function SuperType(name, info) {
        // 实例属性(基本类型)
        this.name = name || 'Super'
        // 实例属性(引用类型)
        this.info = info || ['Super']
        // 实例方法
        this.getName = function() { return this.name }
    }
    // 原型方法
    SuperType.prototype.getInfo = function() { return this.info }
    
    // 原型继承
    function ChildType(message) { this.message = message }
    ChildType.prototype = new SuperType('Child', ['Child'])
    
    // 在调用子类构造函数时,无法向父类构造函数传递参数
    var child = new ChildType('Hello')
    
    // 子类实例可以访问父类的实例方法和原型方法
    console.log(child.getName()) // Child
    console.log(child.getInfo()) // ["Child"]
    
    // 所有子类实例共享父类的引用属性
    var other = new ChildType('Hi')
    other.info.push('Temp')
    console.log(other.info) // ["Child", "Temp"]
    console.log(child.info) // ["Child", "Temp"]
    
    • 缺点:在调用子类构造函数时,无法向父类构造函数传递参数
    • 优点:子类实例可以访问父类的实例方法和原型方法
    • 缺点:所有子类实例共享父类的引用属性

    1.2 构造继承

    方法:在子类的构造函数调用父类的构造函数,并将 this 指向子类实例

    原理:在构造子类时,调用父类的构造函数初始化子类的属性和方法

    function SuperType(name, info) {
        // 实例属性(基本类型)
        this.name = name || 'Super'
        // 实例属性(引用类型)
        this.info = info || ['Super']
        // 实例方法
        this.getName = function() { return this.name }
    }
    // 原型方法
    SuperType.prototype.getInfo = function() { return this.info }
    
    // 构造继承
    function ChildType(name, info, message) {
        SuperType.call(this, name, info)
        this.message = message
    }
    
    // 在调用子类构造函数时,可以向父类构造函数传递参数
    var child = new ChildType('Child', ['Child'], 'Hello')
    
    // 子类实例可以访问父类的实例方法,但是不能访问父类的原型方法
    console.log(child.getName()) // Child
    console.log(child.getInfo()) // Uncaught TypeError
    
    // 每个子类实例的属性独立存在
    var other = new ChildType('Child', ['Child'], 'Hi')
    other.info.push('Temp')
    console.log(other.info) // ["Child", "Temp"]
    console.log(child.info) // ["Child"]
    
    • 优点:在调用子类构造函数时,可以向父类构造函数传递参数
    • 缺点:子类实例可以访问父类的实例方法,但是不能访问父类的原型方法,因此无法做到函数复用
    • 优点:每个子类实例的属性独立存在

    1.3 组合继承

    方法:同时使用原型继承和构造继承,综合两者的优势所在

    原理:通过原型继承实现原型属性和原型方法的继承,通过构造继承实现实例属性和实例方法的继承

    function SuperType(name, info) {
        // 实例属性(基本类型)
        this.name = name || 'Super'
        // 实例属性(引用类型)
        this.info = info || ['Super']
        // 实例方法
        this.getName = function() { return this.name }
    }
    // 原型方法
    SuperType.prototype.getInfo = function() { return this.info }
    
    // 组合继承
    function ChildType(name, info, message) {
        SuperType.call(this, name, info)
        this.message = message
    }
    ChildType.prototype = new SuperType()
    ChildType.prototype.constructor = ChildType
    
    // 在调用子类构造函数时,可以向父类构造函数传递参数
    var child = new ChildType('Child', ['Child'], 'Hello')
    
    // 子类实例可以访问父类的实例方法和原型方法
    console.log(child.getName()) // Child
    console.log(child.getInfo()) // ["Child"]
    
    // 每个子类实例的属性独立存在
    var other = new ChildType('Child', ['Child'], 'Hi')
    other.info.push('Temp')
    console.log(other.info) // ["Child", "Temp"]
    console.log(child.info) // ["Child"]
    
    • 优点:在调用子类构造函数时,可以向父类构造函数传递参数
    • 优点:子类实例可以访问父类的实例方法和原型方法
    • 优点:每个子类实例的属性独立存在
    • 缺点:在实现组合继承时,需要调用两次父类构造函数

    2.1 原型式继承

    方法:实现一个函数,传入已有对象,在函数内部将新对象的原型指向原有对象,最后返回新对象

    原理:返回的新对象继承原有对象,然后根据需求对得到的对象加以修改即可

    var superObject = {
        name: 'Super',
        info: ['Super'],
        getName: function() { return this.name }
    }
    
    // 原型式继承
    function object(o) {
        function F() {}
        F.prototype = o
        return new F()
    }
    
    // 创建子类实例必须基于一个已有对象
    var childObject = object(superObject)
    
    // 根据需求对得到的对象加以修改
    childObject.message = 'Hello'
    
    // 新创建的实例可以访问已有对象的实例属性和实例方法
    console.log(childObject.name) // Super
    console.log(childObject.getName()) // Super
    
    // 所有新创建的实例共享已有对象的引用属性
    var otherObject = object(superObject)
    otherObject.info.push('Temp')
    console.log(otherObject.info) // ["Child", "Temp"]
    console.log(childObject.info) // ["Child", "Temp"]
    
    • 要求:创建子类实例必须基于一个已有对象

    • 缺点:所有新创建的实例都会重新定义已有对象的实例方法,因此无法做到函数复用

    • 缺点:所有新创建的实例共享已有对象的引用属性

    2.2 寄生式继承

    方法:创建一个用于封装继承过程的函数,在函数内部以某种方式增强对象,且最后返回对象

    原理:基于原型式继承,类似于工厂模式,将增强对象的过程封装到一个函数中

    var superObject = {
        name: 'Super',
        info: ['Super'],
        getName: function() { return this.name }
    }
    
    // 寄生式继承
    function object(o) {
        function F() {}
        F.prototype = o
        return new F()
    }
    function objectFactory(o) {
        var clone = object(o)   // 创建对象
        clone.message = 'Hello' // 增强对象
        return clone            // 返回对象
    }
    
    // 创建子类实例必须基于一个已有对象
    var childObject = objectFactory(superObject)
    
    // 新创建的实例可以访问已有对象的实例属性和实例方法
    console.log(childObject.name) // Super
    console.log(childObject.getName()) // Super
    
    // 所有新创建的实例共享已有对象的引用属性
    var otherObject = object(superObject)
    otherObject.info.push('Temp')
    console.log(otherObject.info) // ["Child", "Temp"]
    console.log(childObject.info) // ["Child", "Temp"]
    
    • 要求:创建子类实例必须基于一个已有对象

    • 缺点:所有新创建的实例都会重新定义已有对象的实例方法,因此无法做到函数复用

    • 缺点:所有新创建的实例共享已有对象的引用属性

    3 寄生式组合继承

    方法:借用寄生式继承的思路,结合组合继承的方法,解决组合继承中需要调用两次父类构造函数的问题

    原理:通过构造继承实现实例属性和实例方法的继承,通过寄生式继承实现原型属性和原型方法的继承

    不用为了指定子类的原型而调用父类的构造函数,而是使用寄生式继承来继承父类的原型,然后指定给子类的原型

    function SuperType(name, info) {
        // 实例属性(基本类型)
        this.name = name || 'Super'
        // 实例属性(引用类型)
        this.info = info || ['Super']
        // 实例方法
        this.getName = function() { return this.name }
    }
    // 原型方法
    SuperType.prototype.getInfo = function() { return this.info }
    
    // 寄生式组合继承
    function object(o) {
        function F() {}
        F.prototype = o
        return new F()
    }
    function objectFactory(childType, superType) {
        var prototype = object(superType.prototype) // 创建对象
        prototype.constructor = childType           // 增强对象
        childType.prototype = prototype             // 将父类原型指定给子类原型
    }
    function ChildType(name, info, message) {
        SuperType.call(this, name, info)
        this.message = message
    }
    objectFactory(ChildType, SuperType)
    

    寄生式组合继承是 JavaScript 中最常用的继承方式,ES6 中新增的 extends 底层也是基于寄生式组合继承的

    【 阅读更多 JavaScript 系列文章,请看 JavaScript学习笔记

  • 相关阅读:
    旋转坐标转换的矩阵推导
    Union的妙用和注意
    OpenGL环境搭建Windows+Mac+Linux
    RichLabel基于Cocos2dx+Lua v3.x
    Cocos2dx+lua中Color参数的坑
    常见开源协议
    lua序列化(支持循环引用)
    lua标签解析器
    Markdown基本语法
    python3爬取墨迹天气并发送给微信好友,附源码
  • 原文地址:https://www.cnblogs.com/wsmrzx/p/12375043.html
Copyright © 2011-2022 走看看