• DOM笔记(十三):JavaScript的继承方式


    在Java、C++、C#等OO语言中,都支持两种继承方式:接口继承和实现继承。接口继承制继承方法签名,实现继承则继承实际的方法和属性。在SCMAScript中,由于函数没有签名,所以无法实现接口继承,只支持实现继承。

    实现继承主要依靠原型链来实现。

    一、原型链

    原型链是利用原型让一个引用类型继承另一个引用类型的方法,在DOM笔记(十二):又谈原型对象中,描述了构造函数、实例和原型之间的关系:

    每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而每个实例都包含一个指向原型对象的内部指针。如果将原型对象作为另一个构造函数的实例,会如何?

    function SuperType()
    {
        this.property = true;
    }
    SuperType.prototype.getSuperValue=function()
    {
        return this.property;
    };
    function SubType()
    {
        this.subproperty = false;
    }
    // 继承SuperType
    SubType.prototype = new SuperType();
    SubType.prototype.getSubValue=function()
    {
        return this.subproperty;
    };
    var instance = new SubType();
    alert(instance.getSuperValue());  //true

     

    DOM笔记(十一):JavaScript对象的基本认识和创建中提到了,Object类型是所有对象的根元素。所以,SubType也是继承了Object,内部有一个指针指向Object的原型。  现在,他们的关系变成了这样:

    当instance调用getSuperValue()时,会进行搜索:1)在实例中搜索;2)在实例中找不到,就在SubType.prototype中搜索;3)在SubType.prototype中找不到,就在SuperType.prototype中找。找到后就停止搜索,返回结果值。但instance调用toString()时,就一直会搜索到原型链的末端—Object.prototype。

    根据原型链,需要注意的是,instance.constructor不再指向SubType,而是指向了SuperType。

    二、原型继承小结

      1、确定原型跟实例的关系

    DOM笔记(十二):又谈原型对象中提到了isPrototypeOf()方法,另外也可以用instanceof确立这种关系的存在。

    //均返回true
    alert(instance instanceof Object);
    alert(instance instanceof SuperType);
    alert(instance instanceof SubType);
    alert(Object.prototype.isPrototypeOf(instance));
    alert(SuperType.prototype.isPrototypeOf(instance));
    alert(SubType.prototype.isPrototypeOf(instance));

     

    2、当在子类中定义与父类中同名的属性和方法时,父类中对应的属性和方法会被屏蔽。

    function SuperType()
    {
        this.property = true;
    }
    SuperType.prototype.getSuperValue=function()
    {
        return this.property;
    };
    function SubType()
    {
        this.property = false;    //同名属性
    }
    // 继承SuperType
    SubType.prototype = new SuperType();
    SubType.prototype.getSubValue=function()
    {
        return this.property;
    };
    var instance = new SubType();
    alert(instance.getSuperValue());  //false
      3、原型链实现继承,不能使用对象字面量创建原型方法,防止重写原型链。
    function SuperType()
    {
        this.property = true;
    }
    SuperType.prototype.getSuperValue=function()
    {
        return this.property;
    };
    function SubType()
    {
        this.subproperty = false;
    }
    // 继承SuperType
    SubType.prototype = new SuperType();
    // 使用字面量添加新方法,导致上一行无效
    SubType.prototype={
        getSubValue:function()
        {
            return this.subproperty;
        }
    };
    var instance = new SubType();
    alert(instance.getSuperValue());  //error:undefined is not a function

     

      4、原型继承存在两个问题:1)引用类型的原型属性会被所有实例共享;2)原型继承时,不能通过子类的实例向父类的构造函数中传递参数。

    function SuperType()
    {
        this.colors = ["red","blue","white"];
    }
    function SubType(){ }
    // 继承SuperType
    SubType.prototype = new SuperType();
    var instance1 = new SubType();
    instance1.colors.push("black");
    alert(instance1.colors);  //red,blue,white,black
    var instance2 = new SubType();
    alert(instance2.colors);  //red,blue,white,black

     

    instance1和instance2共享了colors数组属性(引用类型)。

    三、其他继承方式

    1、借用构造函数

    利用原型继承时,引用类型的原型属性会被所有实例共享。但是借用构造函数就能避免这种问题,并且能够利用apply()和call()传递参数给父类的构造函数:

    function SuperType()
    {
        this.colors = ["red","blue","white"];
    }
    function SubType()
    {
        // 继承SuperType
        SuperType.call(this);
    }
    var instance1 = new SubType();
    instance1.colors.push("black");
    alert(instance1.colors);  //red,blue,white,black
    var instance2 = new SubType();
    alert(instance2.colors);  //red,blue,white
      2、组合继承(推荐)
      组合继承即原型继承+构造函数继承:原型链实现对原型属性和方法的继承,构造函数实现对实例属性的继承。
    function SuperType(name)
    {
        this.name=name;
        this.colors = ["red","blue","white"];
    }
    SuperType.prototype.sayName=function()
    {
        alert(this.name);
    };
    function SubType(name,age)
    {
        // 继承SuperType属性,并传递参数给父类的构造函数
        SuperType.call(this,name);              //第一次调用SuperType()
        this.age=age;
    }
    
    // 继承SuperType方法
    SubType.prototype = new SuperType();      //第二次调用SuperType()
    SubType.prototype.constructor = SubType;
    SubType.prototype.sayAge=function()
    {
        alert(this.age);
    };
    var instance1 = new SubType("dwqs",20);
    instance1.colors.push("black");
    alert(instance1.colors);  //red,blue,white,black
    instance1.sayName();  //dwqs
    instance1.sayAge();  //20
    var instance2 = new SubType("i94web",25);
    alert(instance2.colors);  //red,blue,white
    instance2.sayName();  //i94web
    instance2.sayAge();  //25

     

      为了确保SuperType构造函数不会重写子类的原型,建议在调用父类构造函数之后再添加子类的属性。
      组合继承的不足之处在于:1)无论什么情况,都会调用两次父类构造函数;2)子类需要重写父类中的属性,并且会包含两组name和colors属性(父类属性):一组在实例上,一组在原型上
      3、原型式继承
      此种方式是借用原型,基于已有的对象创建新的对象,同时不必创建自定义类型。但是需要借助于如下的一个函数:
    function object(o)
    {
        function F(){}
        F.prototype=o;
        return new F();
    }
      o是已经存在的对象,返回的对象与o有相同的属性和方法。
    var person={
    name:“dwqs”,
    colors:["red","blue","white"]
    };
    var per1 = object(person);
    alert(per1.name); //dwqs
    // 覆盖已有的属性
    per1.name = “i94web”;
    
    per1.age = 20; //新增属性
    alert(per1.age); //20
    per1.colors.push(“black”);
    
    alert(per1.name); //i94web
    alert(per1.colors); //red,blue,white,black
    var per2 = object(person);
    alert(per2.name); //dwqs
    // 所有实例共享引用类型的属性
    alert(per2.colors); //red,blue,white,black

     

    其混乱关系如下:

    per1 和per2会共享colors属性(引用类型)。在ECMAScript 5中,Object.create(obj,[props])规范了这种方式:obj是已经存在的对象,props可选,是一个包含额外属性的对象,格式 与Object.defineProperties()的第二个参数格式一样。

    var person={
        name:"dwqs",
        colors:["red","blue","white"]
    };
    var per1 = Object.create(person,{
        age:{
            value:20
        }
    });
    alert(per1.age);  //20

     

    4、寄生式继承

    该方式与原型式继承差不多,也要借用object函数,然后在内部已某种方式来增强对象。

    function object(o)
    {
        function F(){}
        F.prototype=o;
        return new F();
    }
    var person={
        name:"dwqs",
        colors:["red","blue","white"]
    };
    function createObj(obj)
    {
        var clone = object(obj);
        // 增强对象
        clone.age = 20;
        clone.Hello=function()
        {
            alert("Hello,my age is "+this.age);
        };
        return clone;
    }
    var per1 = createObj(person);
    per1.Hello();   //Hello,my age is 20

     

      5、寄生组合式继承

    在第2中方式,组合继承虽然时JavaScript钟最常用的方式,但是也存在不足,上面已经提到,不再赘述。而寄生组合式继承能解决这两个不足,其基本模式如下:

    function object(o)
    {
        function F(){}
        F.prototype=o;
        return new F();
    }
    function inheritPrototype(subType,superType)
    {
        var obj = object(superType.prototype);  //创建对象
        obj.constructor = subType;                      //增强对象
        subType.prototype = obj;                        //指定对象
    }

     

    重写组合继承中的示例:

    function object(o)
    {
        function F(){}
        F.prototype=o;
        return new F();
    }
    function inheritPrototype(subType,superType)
    {
        var obj = object(superType.prototype);  //创建对象
        obj.constructor = subType;                      //增强对象
        subType.prototype = obj;                        //指定对象
    }
    function SuperType(name)
    {
        this.name=name;
        this.colors = ["red","blue","white"];
    }
    SuperType.prototype.sayName=function()
    {
        alert(this.name);
    };
    function SubType(name,age)
    {
        // 继承SuperType属性,并传递参数给父类的构造函数
        SuperType.call(this,name);
        this.age=age;
    }
    inheritPrototype(SubType,SuperType);
    SubType.prototype.sayAge=function()
    {
        alert(this.age);
    };
    var instance1 = new SubType("dwqs",20);
    instance1.colors.push("black");
    alert(instance1.colors);  //red,blue,white,black
    instance1.sayName();  //dwqs
    instance1.sayAge();  //20
    var instance2 = new SubType("i94web",25);
    alert(instance2.colors);  //red,blue,white
    instance2.sayName();  //i94web
    instance2.sayAge();  //25

     

    只调用了一次SuperType()构造函数,并且避免了SuperType.prototype上创建多余和不必要的属性,原型链保持不变,能够使用instanceof和isPrototypeOf()方法。

    原文首发:http://www.ido321.com/1381.html


  • 相关阅读:
    使用vue-cli初始化vue项目
    easyUI使用datagrid-detailview.js实现二级列表嵌套
    easyUI使用dailog实现弹出框带表单功能
    vue进行文件下载
    TypeError: Cannot read property 'length' of null
    org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'null' to required type 'double' for property 'band'; nested exception is org.springframework.core.convert.Con
    桑叶黑芝麻糊,从头到脚通补
    家庭中成药使用方法一览表
    儿科常见疾病的中成药疗法
    常见病饮食宜忌速查手册
  • 原文地址:https://www.cnblogs.com/ido321/p/4188174.html
Copyright © 2020-2023  润新知