• JavaScript 一文彻底理解并掌握ES5继承


    继承的目的是什么?结果是什么?
    子类继承父类,子类实例拥有和父类实例相同的属性

    父类实例的属性来自于哪里?

    1. 构造函数
    2. 原型对象

    因此继承的实现应该做到以下两件事情

    1. 继承父类构造函数设置的属性: 借用父类构造函数使用apply/call绑定this为子类实例,使得子类实例具有父类实例相同属性
    2. 继承父类的原型属性: 将父类的原型属性设置到子类原型上去,通过new关键字调用继承父类原型属性

    原型继承

    思路是继承将父类实例放在构造函数原型上,父类实例作为子类原型
    缺陷是所有子类共用一个原型对象,引用类型属性的地址相同。
    只继承了原型属性,但缺失了调用父类构造函数为子类实例设置属性的这一过程,导致继承的属性全部来自于原型

        function object(o){
            function F(){}
            F.prototype = o
            return new F()
        }
    
        const o = {a:[1,2,3]}
        const newO = object(o)
        newO.a.push(4)
        console.log(newO,o); // [1,2,3,4]
        
    

    组合继承

    原型继承+借用父类构造函数设置实例属性
    用的最多,缺陷是调用了两次父类构造函数

        function SuperClass() { }
        function SubClass() {
            SuperClass.apply(this, Array.from(arguments)) // 借用构造函数
        }
        SubClass.prototype = new SuperClass() // 继承原型属性
    

    寄生式继承

    寄生式继承基于原型继承, 主要体现在给实例做拓展与与增强
    缺陷在于增强实例的过程中不能做到函数复用,使得效率降低,这一点和构造函数设置实例属性是一致的

        function object(o){
            function F(){}
            F.prototype = o 
            const obj = new F()
            // 拓展、增强
            obj.fn = ()=>console.log('fn');
            return obj
        }
    

    寄生组合式继承

    寄生组合式继承解决了组合继承调用两次父类构造函数使得开销过大的问题

        function extend(SubClass, SuperClass) {
            function F() { }
            F.prototype = SuperClass.prototype
            SubClass.prototype = new F()
    
            SubClass.prototype.constructor = SubClass
            SubClass.SuperClass = SuperClass
        }
    
        // 使用
        function Child() {
            Child.SuperClass.apply(this, arguments)
        }
        function Parent() { this.a = 1 }
    
        extend(Child, Parent)
        console.log(new Child());
    

    总结

    原型继承,所有实例共用一个原型对象,继承了原型属性,但缺失了调用父类构造函数为子类实例设置属性的这一过程,导致继承的属性全部来自于原型,缺少了文章开头提到的第1点

    组合继承,用的最多的方案,虽然1、2都满足,但是存在调用了两次父类构造函数的问题

    寄生式继承,在原型继承的基础上,在实例创建的过程中给实例做拓展和增强,缺陷为并没有调用父类构造函数设置实例属性,复用性差,不满足1

    寄生组合式继承,通过一个中间函数F,解决了组合继承中父类构造函数调用两次的问题;在子类实例创建过程中调用父类构造函数,解决了原型继承和寄生式继承中没有用父类构造函数为子类实例设置属性的问题。 1、2都满足。

    拓展: ES5继承与ES6继承的区别

    引用阮一峰的 ECMAScript6入门 的class继承篇:
    子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
    ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

    简而言之: es5是儿子出生了再去找爹,es6是先找到爹再生儿子

  • 相关阅读:
    《面向对象编程》c++ primer 第15章
    extern的作用(综合网络)
    C程序内存区域分配(5个段作用)
    鼓舞自己的名言
    快速指数算法 和 求逆元算法
    HP Xeon 55xx上GPU的带宽问题
    Ubuntu12.04 安装ibusfbterm0.9.1
    Win7下读写Ext2/Ext3/Ext4文件系统
    fbv安装 为console添加背景图片
    CentOS6/7安装fcitx4.2.5
  • 原文地址:https://www.cnblogs.com/ltfxy/p/16400903.html
Copyright © 2020-2023  润新知