zoukankan      html  css  js  c++  java
  • 原型及继承

    原型

    每个函数都有一个prototype属性,指向一个对象,这个对象专门保存特定类型的所有实例【共有的属性和方法】。

    所有原型对象都会自动获得constructor属性,指向构造函数。

    在调用构造函数创建新实例对象时,会自动设置新实例的内部属性[[Prototype]]指向构造函数的prototype属性指向的对象。

    所有对象都有[[Prototype]]属性:通过构造函数创建的对象的原型对象为构造函数的prototype属性所指的对象,通过字面量创建的对象的原型对象为Object.prototype所指向的对象。构造函数也是对象,其原型对象为Object.prototype所指向的对象。

    所有子对象共有的成员属性,都要保存在构造函数的原型对象中。即一次定义,重复使用。

     

    每当代码读取对象的某个属性时,会执行一次搜索,目标是给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了给定的属性,则立即停止继续搜索并返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象。如果在原型对象中查找到该属性,则返回该属性的值。

    实例中与原型同名的属性会屏蔽原型中的那个属性,所以不能在实例中修改原型中的属性,必须在原型上修改。

    组合使用构造函数和原型:构造函数用于定义实例属性,原型用于定义方法(引用类型)和共享的属性。这样每个实例都会获得一份自己独立的实例属性副本,彼此间互不影响。

    function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.friends = [“Shelby”, “Court”];
    }
    
    Person.prototype = {
        constructor: Person,
        sayName: function () {
            alert(this.name);
        }
    };
    
    var person1 = new Person(“Nicholas”, 29, “Software Engineer”);
    var person2 = new Person(“Greg”, 27, “Doctor”);
    person1.friends.push(“Van”);
    alert(person1.friends); //”Shelby,Court,Van”
    alert(person2.friends); //”Shelby,Court”
    alert(person1.friends === person2.friends); //false
    alert(person1.sayName === person2.sayName); //true 

    原型相关API:

    • 获取原型对象:

      从构造函数获得原型对象: 构造函数.prototype

      从子对象获得父级原型对象:

        子对象.__proto__(有兼容性问题)

              Object.getPrototypeOf({...}) === Object.prototype

    • 判断原型对象是否在实例的原型链上:

        obj.isPrototypeOf(实例)   //Array.prototype.isPrototypeOf([])判断数组

    • 自有属性和共有属性:

        obj.hasOwnProperty()     //检测一个属性是否存在于实例中。

    • in关键字:property in obj

        在obj的原型链上查找指定属性名property

    • 删除对象的属性:delete obj.property  //不能删除共有属性                       

    原型链

    本质是重写原型对象,让一个类型的原型对象等于另一个类型的实例

    function SuperType(){
        this.property = true;
    }
    
    SuperType.prototype.getSuperValue = function(){
        return this.property;
    };
    
    function SubType(){
        this.subproperty = false;
    }
    
    //重写原型对象,以新的原型对象替换默认原型,实现继承。
    //不能是父类的原型对象,只能是实例。否则子类修改原型属性时会影响父类。
    SubType.prototype = new SuperType();
    SubType.prototype.getSubValue = function (){
        return this.subproperty;
    };
    
    var instance = new SubType();
    console.log(instance.getSuperValue()); //true

    继承的实现

    1. 单个继承

             Object.create(Proto [,propertiesObject]);  // 使用指定的原型对象和属性创建一个新对象。

    等价于:

    function object(o){
        function F(){};
        F.prototype=o;
        return new F();
    }

      Object.setPrototypeOf(Plane.prototype,Flyer.prototype);  //Plane.prototype.__proto__=Flyer.prototype;

      //直接设置一个对象的内部[[Prototype]]属性到另一个对象或null,实现继承。子类型的原型对象依然存在,子类型原型中重写父类型fly方法,不会影响父类型的原型对象。有性能问题,慎用

     2.原型链方式

    因为有可能重写或添加子类型的方法,为了保证之后创建的所有子类型都继承同一个超类型,一定要在创建新实例之前修改原型对象。

      子类型构造函数.prototype=超类型的实例;

    注意:

      通过原型链实现的继承,不能使用对象字面量创建原型方法,因为这样会重写原型链。

      不能向超类型的构造函数中传递参数。

      如果原型被重写,最好同时修改原型的constructor属性。

      如果超类型的属性中有引用类型值,所有实例共享一个属性,不好。

    3. 组合继承

    (1)在子类型构造函数内部用call/apply调用父类型的构造函数。

    function SubType(sColor, sName) {
        SupType.apply(this, arguments); 
        this.name = sName;
    }

    注意:arguments指SubType接收到的所有参数,SupType从0位开始按顺序读取。所以SubType构造函数的参数中前面是SupType的参数,之后才是SubType用到的参数(SubType能够用标识符识别参数的值,而SupType只能通过位序使用)。

    (2)将父类型的实例赋值给子类型的原型,继承父类共享的属性和方法。

      SubType.prototype=new SuperType();
      SubType.prototype.constructor=SubType;     //原型重写时最好同时修改

    注意:组合继承会两次调用父类构造函数,在子类实例和原型中会有重复的属性。

    4.寄生式组合继承

    通过借用构造函数来继承属性,通过原型链的混合形式来继承方法。不必为了指定子类型的原型而调用超类型的构造函数,只需父类型的原型副本。

    //使用指定的原型对象创建一个新的空对象作为中间体,类似于Object.create() 方法

    //使用指定的原型对象创建一个新的空对象作为中间体,类似于Object.create() 方法
    function cloneProto(o){
        function F(){}
        F.prototype = o;
        return new F();
    }
    
    //子类型的原型为上述空对象
    function inheritPrototype(subType, superType){
        var prototype = cloneProto(superType.prototype);
        prototype.constructor = subType;   
        subType.prototype = prototype; 
    }
    
    function SuperType(name){
        this.name = name;
        this.colors = [“red”, “blue”, “green”];
    }
    SuperType.prototype.sayName = function(){
        alert(this.name);
    };
    
    function SubType(name, age){
        SuperType.call(this, name); //继承了父类型,同时向父类型构造函数传递了参数
        this.age = age; 
    }
    
    //不能直接继承父类型的原型对象(公用一个原型,子类型修改会影响父类型)
    //也不能直接继承父类型的实例(父类型的实例属性也会被子类型实例继承,需要再次覆盖)
    //通过一个中间对象只继承父类型的原型方法和属性,子类型修改时也不影响父类型
    inheritPrototype(SubType, SuperType);
    
    SubType.prototype.sayAge = function(){
        alert(this.age);
    };
  • 相关阅读:
    039、Data Volume 之 bind mount (2019-02-28 周四)
    038、Docker 的两类存储资源(2019-02-27 周三)
    037、外部网络如何访问容器 (2019-02-26 周二)
    036、容器如何访问外部世界 (2019-02-25 周一)
    035、容器间通信的三种方式(2019-02-22 周五)
    034、理解容器之间的连通性(2019-02-21 周四)
    033、如何自定义容器网络(2019-02-20 周三)
    032、学容器必须懂bridge网络(2019-02-19 周二)
    031、none和host网络的适用场景(2019-02-18 周一)
    030、实现容器的底层技术(2019-01-25 周五)
  • 原文地址:https://www.cnblogs.com/kevin2chen/p/7080310.html
Copyright © 2011-2022 走看看