海拔6.3持续

成千上万OO语言都支持少数种植持续方式:接口继承与落实继承.接口继承只持续方法签名,而落实持续则继续实际的方法.由于函数没有签字,在ECMAScript中无法落实接口继承,只支持落实连续,而且根本靠原型链来实现继承.

6.3.1 原型链

原型链,其主导思维是使原型为一个援类型继承另一个援类型的性质和方法.

构造函数,原型和实例的涉及:每个构造函数都起一个原型对象,原型对象还蕴涵一个针对构造函数的指针,而实例都蕴含一个对原型对象的内指针.如果我们深受原型对象等另一个品种的实例,那么原型对象将富含一个对任何一个原型的指针,相应地,另一个原型中吗蕴含着一个对准任何一个构造函数的指针.如此斑斑推进,就组织了实例与原型的链条.这即是所谓原型链的基本概念.

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();
    console.log(instance.getSuperValue());//true

SubType继承了SuperType,而持续是透过创建SuperType的实例,并拿该实例赋给SubType.prototype实现的.实现的实质是又写原型对象,代的因一个新类型的实例.

ECMAScript 1

getSuperValue()方法仍然还于SuperType.prototype中,但property则放在SubType.prototype中.这是盖property是一个实例属性,而getSuperValue()则是一个原型方法.既然SubType.prototype现在是SuperType的实例,那么property就厕该实例中了.要留心instance.constructor现在对的凡SuperType,这是因原本SubType.prototype中的constructor被还写了之缘故.

点例子中,调用instance.getSuperValue()会经历三独寻步骤:1)搜索实例,2)搜索SubType.prototype;3)搜索SuperType.prototype,最后一步才会找到该方法.

1.别忘记默认的原型

备函数的默认原型都是Object的实例,因此默认原型都见面含有一个中间指针,指向Object.prototype.这也是持有由定义类型且见面连续toString(),valueOf()等默认方法的根本原因.下面是点例子的整体原型链.

ECMAScript 2

SubType继承了SuperType,而SuperType继承了Object.当调用instance.toString()时,实际上调用的凡保存在Object.prototype中的挺方法.

2.确定原型和实例的涉及

得经过简单种方式来规定原型和实例的关系.

第一种是运用instanceof操作符,只要用者操作符来测试实例与原型链中出现了之构造函数,结果就会见回true.

console.log(instance instanceof Object);//true
    console.log(instance instanceof SuperType);//true
    console.log(instance instanceof SubType);//true

其次种是使isPrototypeof()方法.同样,只要是原型链中出现了的原型,都可以说凡是该原型链派生的实例的原型,因此isPrototypeof()方法也会见返回true;

console.log(Object.prototype.isPrototypeOf(instance));//true
    console.log(SuperType.prototype.isPrototypeOf(instance));//true
    console.log(SubType.prototype.isPrototypeOf(instance));//true

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;
    };

    //重写超类型中的方法
    SubType.prototype.getSuperValue=function(){
        return false;
    }
    var instance=new SubType();
    console.log(instance.getSuperValue());//false

getSuperValue()是原型链中已经存在的一个法,重写是法将丁挡原来的那个方法.当通过SubType的实例调用getSuperValue()时,调用的即是此更定义之艺术;但经SuperType的实例调用getSuperValue()时,还会见持续调用原来死方法.

得在用SuperType的实例替换原型之后,再定义两独方法.

以通过原型链实现连续时,不克应用对象字面量创建原型方法.因为这样做会更写原型链.

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;
    };

    //使用字面量添加新方法,会导致上一行代码无效
    SubType.prototype={
        getSubValue:function(){
            return this.subproperty;
        },
        someotherMethod:function(){
            return false;
        }
    };

    var instance=new SubType();
    console.log(instance.getSuperValue());//Uncaught TypeError: instance.getSuperValue is not a function

出于本的原型包含的凡一个Object的实例,而非SuperType的实例,因此我们考虑着之原型链已经被隔离—SubType和SuperType之间业已没有关联了.

4.原型链的问题.

饱含引用类型值的原型属性会给有着实例所并享
;而及时为亏为何而在构造函数中,而不是原型对象吃定义属性之原因.在经过原型来贯彻连续时,原型实际上会化为任何一个类的实例.于是,原先的实例属性也不怕顺理成章地变成了今日底原型属性了.

function SuperType(){
        this.colors=["red","blue","green"];
    }

    function SubType(){

    }

    //继承了SuperType
    SubType.prototype=new SuperType();

    var instance1=new SubType();
    instance1.colors.push("black");
    console.log(instance1.colors);//["red", "blue", "green", "black"]

    var instance2=new SubType();
    console.log(instance2.colors);//["red", "blue", "green", "black"]

咱们对instance1.colors之改动能够透过instance2.colors反映出来.

原型链的老二个问题是:在创建子类型的实例时,不能够像超类型的构造函数中传递参数.实际上,应该算得没有艺术于不影响有目标实例的景象下,给超类型的构造函数传递参数.

尽着好少会单独使用原型链.

6.3.2 借用构造函数

借构造函数(constructor
stealing),这种技能之主导思想相当简单,即在子类型构造函数的里边调用超类型构造函数.函数只不过是一定环境面临施行代码的目标,因此通过使用apply()和call()方法为可以在(将来)新创办的目标及执行构造函数.

function SuperType(){
        this.colors=["red","blue","green"];
    }

    function SubType(){
        //继承了SuperType
        SuperType.call(this);
    }

    var instance1=new SubType();
    instance1.colors.push("black");
    console.log(instance1.colors);//["red", "blue", "green", "black"]

    var instance2=new SubType();
    console.log(instance2.colors);//["red", "blue", "green"]

1.传递参数

对立原型链,借用构造函数可以在子类型构造函数中往超类型构造函数传递参数.

function SuperType(name){
        this.name=name;
    }
    function SubType(){
        //继承了SuperType,同时还传递了参数
        SuperType.call(this,"Nicholas");

        //实例属性
        this.age=29;
    }

    var instance=new SubType();
    console.log(instance.name);//Nicholas
    console.log(instance.age);//29

2.借出构造函数的题材

倘只有是借构造函数,那么也是问题—方法还当构造函数中定义,因此函数利用就未能谈起了.而且,在超类型的原型中定义之法子,对子类型而言也是不可见的,结果有所项目且只好借构造函数模式.

6.3.3 组合继承

整合继承(combination
inheritance),有时候也被做伪经典延续,指的凡拿原型链和假构造函数的技能整合及均等片,从而发挥双方的丰富之平等种持续模式.其背后的笔触是采取原型链实现对原型属性与措施的持续,而经过借用构造函数来贯彻对实例属性之继承.这样,既通过在原型上定义方法实现了函数复用,又能管每个实例都发出它和谐之属性.

function SuperType(name){
        this.name=name;
        this.colors=["red","blue","green"];
    }

    SuperType.prototype.sayName=function(){
        console.log(this.name);
    };

    function SubType(name,age){
        //继承属性
        SuperType.call(this,name);

        this.age=age;
    }

    //继承方法
    SubType.prototype=new SuperType();
    SubType.prototype.constructor=SubType;
    SubType.prototype.sayAge=function(){
        console.log(this.age);
    }

    var instance1=new SubType("Nicholas",29);
    instance1.colors.push("black");
    console.log(instance1.colors);//["red", "blue", "green", "black"]
    instance1.sayName();//Nicholas
    instance1.sayAge();//29

    var instance2=new SubType("Greg",27);
    console.log(instance2.colors);//["red", "blue", "green"]
    instance2.sayName();//Greg
    instance2.sayAge();//27

组成继承避免了原型链和借构造函数缺陷,融合了她的长处,成为JavaScript中尽常用的持续模式.而且,instanceof和isPrototypeOf()也克用于识别基于组合继承创建的对象.

6.3.4原型式继承

ECMAScript
5经新增Object.create()方法规范化了原型式继承.这个措施接收两只参数:一个作为新对象原型的靶子和(可选的)一个乎新对象定义额外属性的对象.在传唱一个参数的状况下,Object.create()与object()方法的一言一行相同.

var person={
        name:"Nicholas",
        friends:["Shelby","Court","Van"]
    };

    var anotherPerson=Object.create(person);
    anotherPerson.name="Greg";
    anotherPerson.friends.push("Rob");

    var yetAnotherPerson=Object.create(person);
    yetAnotherPerson.name="Linda";
    yetAnotherPerson.friends.push("Barbie");

    console.log(person.friends);//["Shelby", "Court", "Van", "Rob", "Barbie"]

Object.create()方法的次独参数和Object.defineProperties()方法的老二个参数格式相同:每个属性都是由此祥和之叙说符定义的.以这种方法指定的外性质都见面挂原型对象及之以及名属性.

var person={
        name:"Nicholas",
        friends:["Shelby","Court","Van"]
    };

    var anotherPerson=Object.create(person,{
        name:{
            value:"Greg"
        }
    });

    console.log(anotherPerson.name);//Greg

6.3.5 寄生式继承

构成继承是JavaScript最常用之接续模式;不过,它也产生温馨的不足.组合继承最要命之题目不怕是无什么情形下,都见面调用两不好超类型构造函数:一不良是当创造子类型原型的时段,另一样不行是以子类型构造函数内部.没错,子类型最终见面含有超类型对象的全体实例属性,但我们不得不以调用了品种构造函数时再也写这些属于性.

function SuperType(name){
        this.name=name;
        this.colors=["red","blue","green"];
    }

    SuperType.prototype.sayName=function(){
        console.log(this.name);
    };

    function SubType(name,age){
        SuperType.call(this,name);//第二次调用SuperType()

        this.age=age;
    }

    SubType.prototype=new SuperType();//第一次调用SuperType()
    SubType.prototype.constructor=SubType;
    SubType.prototype.sayAge=function(){
        console.log(this.age);
    }

ECMAScript 3

所谓寄生组合继承,即通过借用构造函数来继承属性,通过原型链的混成形式来持续方法.其幕后的思绪是:不必为了借助定子类型的原型而调用超类型的构造函数,我们所要之但就是是超类型原型的一个副本而已.本质上,就是以寄生式继承来继续超类型的原型,然后再次用结果指定给子类型的原型.

寄生组合式继承的基本模式ECMAScript如下所示:

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

此示例中的inheritPrototype()函数实现了寄生组合式继承的太简便形式.这个函数接收两独参数:子类型构造函数和超类型构造函数.在函数内部,第一步是创办超类型原型的一个入本.第二步是也创造的副本添加constructor属性,从而弥补因再也写原型而去的默认的constructor属性.最后一步,将新创办的对象(即副本)赋值给子类型的原型.这样,我们虽可以就此调用inheritPrototype()函数的语,去替换前面例子中呢子类型原型赋值的言语了.

开发人员普通认为寄生组合式继承是引用类型最了不起的继续范式.

6.4 小结

ECMAScript支持面向对象(OO)编程,但未采用项目或者接口.对象好在代码执行过程被创造同增强,因此所有动态性而非严加定义的实体.在并未像样的情景下,可以使用下列模式创建对象.

工厂模式,使用简单的函数创建对象,为目标上加属性和法,然后返回对象.这个模式后来于构造函数模式所取代.

构造函数模式,可以创造于定义引用类型,可以像创建内置对象实例一样采取new操作符.不了,构造函数模式吗产生缺点,即其的每个成员还爱莫能助获得复用,包括函数.由于函数可以不囿于为任何对象(即同对象拥有松弛耦合的特性),因此并未理由不在多独对象中一块享函数.

原型模式,使用构造函数的prototype属性来指定那些应该共享的性能与方法.组合以构造函数模式以及原型模式时,使用构造函数定义实例属性,而下原型定义共享的习性和方法.

JavaScript主要透过原型链实现继承.原型链的布局是通过以一个项目的实例幅值给其它一个构造函数的原型实现的.这样,子类型就会访问超类型的兼具属性与方式,这一点和基本类的继承很相似.原型链的题目是目标的便共享有继续的特性与方,因此无适宜单独使用.解决之题目的艺是假构造函数,同时还能够担保单独使用构造函数模式来定义类型.使用最多之连续模式是组成继承,这种模式应用原型链继承共享的属性与方法,而由此借用构造函数继承实例属性.

除此以外,还在下列可供应选择的后续模式.

原型式继承,可以以无需预先定义构造函数的场面下实例继承,其真相是执行对加对象的浅复制.而复制得到的副本还好获取越来越改造.

寄生式继承,与原型式继承非常相像,也是着力有对象要少数信息创建一个对象,然后增强对象,最后回到对象.为了缓解做继承模式由于频繁调用超类型构造函数而招致的低位效率问题,可以以之模式和整合继承并使用.

寄生组合式继承,集寄生式继承与做继承的长处和孤单,是兑现基于项目继承的顶灵方式.