面向对象编程---继承

来源:互联网 发布:mac地址修改软件下载 编辑:程序博客网 时间:2024/06/05 03:19
    继承是面向对象中一个比较核心的概念。其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。而ECMAScript只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成。

继承:

主要通过原型链实现

1.原型链

    基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。    简单回顾一下构造函数,原型和实例的关系:        每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
        function a(){            this.alike = true;        }        a.prototype.saya=function(){            return this.alike;        }        function b(){            this.blike = false;        }        b.prototype = new a();(注意不能和下面的sayb方法颠倒顺序,否则将切断和sayb的关系,相当于重新赋值了)        b.prototype.sayb = function(){            return this.blike;        }        var c = new b();        alert(c.alike)//true,来自c的原型        alert(c.saya)//true,来自c的原型的原型,(c的原型是a的实例,所以c的原型的原型也就是a的原型)
    a,b既是构造函数也是实例(这里用大写表示构造函数,小写表示实例)    A-----prototype----->A的原型对象-----constructor----->A    B-----prototype----->B的原型对象(A的实例a)-----[[prototype]]----->A的原型对象-----constructor----->A    c(B的实例)-----[[prototype]]----->c的原型对象(B的原型对象(A的实例a))-----[[prototype]]----->A的原型对象-----constructor----->A    所以搜索c的属性和方法时,会先找c(B的实例),没有的话向上找c的原型对象(B的原型对象(A的实例a)),没有的话向上找A的原型对象,一直到Object

1).原型与实例的关系

    接上面的例子:    alert(c instanceof b)//true    alert(c instanceof a)//true    alert(c instanceof Object)//true

2).谨慎定义方法

        function a(){            this.alike = true;        }        a.prototype.saya=function(){            return this.alike;        }        function b(){            this.blike = false;        }        //继承a        b.prototype = new a();        //添加新的方法        b.prototype.sayb = function(){            return this.blike;        }        //重写超类型中的方法        b.prototype.saya = function(){            return "hcd";        }        var c = new b();        alert(c.saya());//"hcd"        在调用saya方法时,找到c的原型便找到了saya方法,就不会去找a.prototype的saya方法。
    注意:用字面量的写法会重写prototype
        function a(){            this.alike = true;        }        a.prototype.saya=function(){            return this.alike;        }        function b(){            this.blike = false;        }        b.prototype = new a();        b.prototype={            sayb:function(){                return this.blike;            },            name:"oooo"        }        var c = new b();        alert(c.saya());        //c.saya is not a function        这是因为c的原型只有sayb方法和name属性,切断了c与a.prototype(也就是b.prototype.prototype)的关系。

3).原型链的问题

        function a(){            this.alike = ["a","b"];        }        a.prototype.saya=function(){            return this.alike;        }        function b(){            this.blike = false;        }        b.prototype = new a();        var c = new b();        var d = new b();        alert(c.alike)//a,b        alert(d.alike)//a,b        c.alike.push("o");        alert(c.alike)//a,b,o        alert(d.alike)//a,b,o        因为资源的共享,导致c向数组插入“o”,d也会受影响。还有就是在实例化时,不能向超类型的构造函数传递参数。 

2.借用构造函数

为了解决引用共享和超类型无法传参的问题,我们采用一种叫借用构造函数的技术,或者成为对象冒充(伪造对象、经典继承)的技术来解决这两种问题。
        function Person(){            this.color = ["a","b"];        }        function O(){            Person.call(this);        }        var person1 = new O();        person1.color.push("pppppp");        alert(person1.color);//a,b,ppppp        var person2 = new O();        alert(person2.color);//a,b        利用call让Perosn在O内执行构造函数,使得每一个O实例都会调用Person方法,都有了自己的color

1).传递参数

        function Person(name){            this.name= name;            this.color = ["a","b"];        }        function O(){            Person.call(this,“hcd“);            this.age = 20;        }        var person1 = new O();;        alert(person1.name);//hcd

2).问题

        方法在构造函数中定义,使的函数无法复用。(Person在O中)。对于子类型而言不知道超类型也就是Person函数是怎样定义的。

3.组合继承

原型链+借用构造函数的模式
        function a(name){            this.name = name;            this.color=["a","b"]        }        a.prototype.saya=function(){            return this.name;        }        function b(name,age){            a.call(this,name);            this.age = age;        }        b.prototype = new a();        var c = new b("hcd",10);        var d = new b("hcd",10);        c.color.push("ppp");        alert(c.color)//a,b,ppp        alert(d.color)//a,b
    首先利用b函数通过call方法继承了a的属性,每一个b的实例都会调用a方法,都有了自己的colors属性和name属性以及age属性,他们互相是没有关系的,然后利用b.prototype = new a();使得b继承了a的属性和方法,因为在b函数中已经有了a的属性,所以b的实例找属性时找到b就会不再往下找a了,而方法都是a.prototype的,所以b的实例的saya方法指向的是一个方法。

4.原型式继承

这种继承借助原型并基于已有的对象创建新对象,同时还不必因此创建自定义类型。
    返回实例的方法:        function Obj(o){            function F(){}            F.prototype = o;            return new F();                 }
    例如:        function obj(o){            function F(){}            F.prototype = o;            return new F();        }        var person = {            name:"hcd",            friends:["a","b"]        }        var person1 = obj(person);        person1.name = "h";        person1.friends.push("h");        var person2 = obj(person);        person2.name = "c";        person2.friends.push("c");        alert(person.friends)//a,b,h,c        person1和person2通过obj函数,创建了原型对象为perosn的两个实例。他们的friends是共享的。
ES5有类似的新的方法Object.create();参数1.作为新对象的原型对象    2.为新对象定义额外属性的对象
        var person = {            name:"hcd",            friends:["a","b"]        }        var person1 = Object.create(person,{             name:{                value:"sfh"            }        })        person1.friends.push("h");        alert(person1.name)//"sfh"

5.寄生式继承

寄生式继承是和原型式继承紧密相关的一种思路。把原型式+工厂模式结合而来,目的是为了封装创建对象的过程。
    function obj(o){            function F(){};            F.prototype = o;            return new F();        }        function createAnthor(o){            var clone = obj(o);//调用obj函数创建一个以o为原型对象的对象实例            clone.say = function(){                alert("pppppppp");            }            //以某种方式增强这个实例            return clone;        }        var person = {            name:"hcd",            friedns:["o","p"]        }        var perosn1 = createAnthor(person);        perosn1.say() 
问题:复用率低,

6.寄生组合式继承

来看一下组合式寄生的问题,就是会调用两次a函数
    function a(name){            this.name = name;            this.colors = ['red'];        }        a.prototype.saya = function(){            console.log(this.name);        }        function b(name.age){            a.call(this,name);               //第二次调用a函数            this.age = age;         }        b.prototype = new a();              //第一次调用a函数        a.prototype.sayb = function(){            console.log(this.age);        }        var c =new b();
    寄生组合式继承:借用构造函数来继承属性,用过原型链的混成模式来继承方法。    背后的思想是不比为了指定子类型的原型而去调用超类型的构造函数。(就是b.prototype = new a()这一句)我们想要额无非是超类型的原型对象的一个副本而已(b.prototype = new a(),其实想要继承a原型对象的方法)本质上就是复制超类型的原型对象(a的原型对象),然后将它指定给子类型的原型对象(指定给b.prototype)。
基本模式:    function obj(o){        function F(){};        F.prototype = o;        return new F();    }    function inheritPrototype(sub,super){        var p = obj(super.prototype);//super一般为构造函数,所以p.prototype相当于是super.prototype,其实p相当于是super的复制版。        (实现p=super)        p.constructor = sub;                sub.prototype = p;        //以上两行代码是为了增强sub和p之间的关系(实现b.prototype = p这样的联系)    }
    其实以上的代码想实现组合式寄生中b.prototype = new a();将p=a,变为b.prototype = new p(),这样就可以不用使用a两次了
        function obj(o){            function F(){};            F.prototype = o;            return new F();        }        function inheritPrototype(sub,super){            var p = obj(super.prototype);   //创建对象                     p.constructor = sub;            //增强对象            sub.prototype = prototype;      //指定对象        }        function Super(name){            this.name = name;            this.colors = ["a","b"];        }        super.prototype.say = function(){            alert(this.name);        }        function Sub(name,age){                 Super.call(this,name);//继承属性            this.age = age;        }        inheritPrototype(Sub,Super);        Sub.prototype.sayage = function(){            alert(this.age)        }
原创粉丝点击