• Javascript面向对象之继承


    与类的创建篇一样,这里先贴出最终代码,再做详细分析:

    // 创建一个父类
    function SuperType(){
        this.company = 'alibaba';
    }

    function SubType(){ SuperType.call(this); // 【1. 调用父类的构造函数】 this.name = 'yunos'; } SubType.prototype = Object.create(SuperType.prototype,{constructor:{value:SubType}}); // 【2. 把父类的原型赋给子类】 var instance = new SubType(); console.log(instance); /*----------------牢记两部关键性的操作,到此为止,已实现理想化的继承------------------------*/ var instance = new SubType(); instance instanceof SubType // true. instance instanceof SuperType // true.

     鉴于部分浏览器尚不支持Object.create, 修正后如下:

    // 辅助函数,解决浏览器不支持Object.create的问题,同时正确指向构造函数
        function createObject(proto, constructor) {
            var newProto;
            if (Object.create) {
                newProto = Object.create(proto);
            } else {
                function Empty(){};
                Empty.prototype = proto;
                newProto = new Empty();
            }
            newProto.constructor = constructor;
            return newProto;
        }
    
    // 创建一个父类
    function SuperType(){
        this.company = 'alibaba';
    }function SubType(){
        SuperType.call(this); // 调用父类的构造函数.
        this.name = 'yunos'; 
    }
    SubType.prototype = createObject(SuperType.prototype, SubType);    // 把父类的原型赋给子类
    
    var instance = new SubType();
    instance instanceof SubType //   true.
    instance instanceof SuperType // true.

    Javascript中最常用的继承模式: 原型链 + 借用构造函数 = 组合继承(又称伪经典继承)。

    原型链继承:

    function SuperType(){
        this.company = 'alibaba';
    }
    SuperType.prototype.getSuperValue = function(){
        return this.company;
    }
    
    function SubType(){
        this.name = 'yunos';
    }
    SubType.prototype = new SuperType();
    
    var instance = new SubType();
    console.log(instance);

    众所周知,Javascript是通过prototype来实现继承的,在此我们创建父类的实例重写了 SubType.prototype。
    换句话说,原来存在于 SuperType 的实例中的所有属性和方法现在也存在于 SubType.prototype中了。
    如下所示:

    当然,不要忘记最后一环,所有的引用类型默认都继承了Object,这一环也是通过原型链来实现的,所有函数默认的原型 { constructor : '...' }都是Object的实例。

    尽管原型链如此强大,可以用它来实现继承,但在实践中我们很少这样做,因为它存在一个严重的问题:父类的属性会成为子类的原型属性,

    而当原型属性包含引用类型值时,该原型属性会被类的所有实例共享,在一个实例上进行的修改,会影响到其他实例。

    借用构造函数继承:

    在解决原型中包含引用类型值多带来问题的过程中,使用一种叫做借用构造函数的技术(有时候也叫做伪造对象或经典继承)。 这种技术思想相当简单,即在子类型构造函数的内部调用父类的构造函数。  —— 函数只不过是在特定环境中执行代码的对象,因此通过使用apply 和 call 方法可以在新创建的对象上执行构造函数,如下所示:

    function SuperType(){
        this.colors = ["red", "blue", "green"];
    }
    
    function SubType(){
        SuperType.call(this);   // 【借调父类的构造函数】
    }
    
    var instance1 = new SubType();
    instance1.colors.push("black");
    alert(instance1.colors);    // red, blue, green, black"
    
    var instance2 = new SubType();
    alert(instance2.colors);    // "red, blue, green"

    通过借调,我们实际上是在(未来将要)新创建的SubType 实例的环境下调用了 SuperType 构造函数。 这样,就会在新SubType对象上执行 SuperType() 函数中定义的所有对象初始化代码。 然后, SubType的每个实例就都会具有自己的 colors 属性的副本了。

    而且它还具有一个很大的优势,即可以在子类构造函数中向父类的构造函数传递参数:

    function SuperType(name){
        this.name = name;
    }
    
    function SubType(){
      SuperType.call(this, "yunos");
      this.age = 29;
    }
    
    var instance = new SubType();
    console.log(instance.name);    // "yunos";
    console.log(instance.age);      // 29

    为了确保父类构造函数不会重写子类型的属性,可以在调用父类构造函数后,再添加应该在子类型中定义的属性。

    组合继承:

    将原型链和借用构造函数的技术组合到一块,从而发挥二者之长,是当前应用最广泛的继承方法:

    function SuperType(){
        this.company = "alibaba";
    }
    SuperType.prototype.getSuperValue = function(){
        return this.company;
    }
    
    function SubType(){
      SuperType.call(this);   // 【借调父类的构造函数】
      this.name = "yunos";
    }
    SubType.prototype = new SuperType();
    
    var instance1 = new SubType();
    console.log(instance1);

     

    虽然并不完美,但类的功能终于具备了,无明显弊端,使用方法也比较简单,所以被广泛使用。

    至此本文应该结束了,但严谨的,偏执的,追求完美的程序员,一直在探求更为完美的实现方式,于是便有了下边的内容:

    Javascript中最理想的继承范式: 原型式 —> 寄生式 —> 寄生组合式继承(广泛应用于YUI、KISSY中)—> 最终继承

    // 辅助方法,用以正确继承父类原型
    function inheritPrototype(subType, superType){
        function F(){};
        F.prototype = superType.prototype;
        var protoObj = new F();
    
    }
  • 相关阅读:
    网线 ------ 交叉线
    ubuntu ------ 网络 ifconfig 不显示IP地址
    STM32L011D4 ----- 低功耗
    List 集合 使用 remove 踩得坑
    Map 集合遍历的4种方法
    elasticsearch 集群详解
    谷歌浏览器添加插件时显示程序包无效:"CRX_HEADER_INVALID" 解决办法
    MySql数据库 优化
    MySql 索引
    Kibana 安装
  • 原文地址:https://www.cnblogs.com/v10258/p/3293179.html
Copyright © 2020-2023  润新知