关于继承

什么是继承?
作为OO语言,有两种继承,接口继承和实现继承。然而js只支持实现继承,实现继承主要是依靠原型链来实现的。我对继承的理解是:子函数具有父函数的属性和方法,就叫继承。
什么是原型链?
《Javascript 高级程序设计》里面这样子解释原型链:
js中,每个函数都有一个[[prototype]]属性,其中保存着函数能够实例化的属性和方法。然后[[prototype]]属性中保存的对象也有其原型,所以形成一个链式,叫原型链。
参考http://www.php100.com/html/webkaifa/javascript/2012/1015/11260.html

接下来我分析一下几个基础的继承。
假设:子函数继承父函数;

1,原型链继承
思想:父函数赋值给子函数的原型。因为父函数的原型中保存着父函数的属性和方法,我们把父函数赋值给子函数,那么父函数的属性和方法也就被赋值给子函数,那么相当于子函数继承了父函数的属性和方法。但是这中继承有一定的缺陷,当我们改变子函数的属性或者重写其方法时,是通过原型来改变的,因为父函数的原型与子函数的原型是一致的,所以父函数的属性和方法也会被改变。

demo:

 function a(){
         this.name="name";
     }
    a.prototype.sayName=function(){
         alert(this.name);
    };
    function b(){
    }
    //继承 a
    b.prototype=new a();
    //实例化
    var c=new b();
    alert(c.name);//name
    c.sayName();//name

原型被改变的情况:

    function a(){
           this.array=["1","2","3"]
     }
    function b(){
    }
    //继承 a
    b.prototype=new a();
    //实例化
    var d=new b();
    var c=new b();
    c.push("4");
    alert(c.array);//1,2,3,4
    alert(d.array);//1,2,3,4  

明显的,原型被改变了。

构造函数实现继承:
思想:在子函数中扩展其作用域,达到了继承父函数的属性的方法。运用call()函数与apply()函数能够完美的实现这点。用call函数和apply函数扩展的作用域是可以自由选择的,也就是能有选择的去继承属性,这点很重要,可以更加高效。 并且不会改变其原型链。但是无法继承父函数的方法。
PS:【call()与apply()函数的用法:
call(this 1,2,3);
apply(this ,[1,2,3]);】

demo:

    function a(){
            this.name="name";
           this.array=["1","2","3"]
     }
    function b(){
        a.call(this,this.array,this.name);
    }

    //实例化
    var d=new b();
    var c=new b();
    c.array.push("4");
    alert(c.array);//1,2,3,4
    alert(d.array);//1,2,3

优点:不改变原型,可以有选择的继承属性。
缺点:不能继承方法。

组合继承:
思想:用原型链实现对属性和方法的继承,用构造函数法来实现对实例属性的继承,并且不会改变原型链。
demo:

    function a(){
         this.name="name";
         this.array=["1","2","3"]
     }
 a.prototype.sayName=function(){
     alert(a.name);
 }
    function b(){
        a.call(this,this.name);//继承name属性   第二次调用父函数
    }
 b.prototype=new a();//继承属性和方法  第一次调用父函数

    //实例化
    var d=new b();
    var c=new b();
    alert(c.name)//name
    c.array.push("4");
    alert(c.array);//1,2,3,4
    alert(d.array);//1,2,3  实现了实例属性的继承

子函数具有了父函数所有的属性和方法,但是在实例化属性时,子函数不会改变其原型。为什么会出现这中情况呢?我分析是这样的,在第一次原型链实现对属性和方法的继承时调用了父函数,继承了所有的属性和方法,然后再执行b()的实例化时,我们又调用了call()方法实例化属性,那么从原型链继承来的属性就会被call()继承的属性覆盖。

优点:不改变原型链。
缺点:两次调用父函数。

原型式继承:
思想:基于原型,可以利用已有的对象创建新的对象,而且不需要去创建自定义类型。为此,用这样的一个函数可以达到目的:

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

这种继承必须要先提供一个对象;
demo:

function object(o){
    function F(){};
    F.prototype=o;
    return new F();
}
 var person={
         name:"name",
         age:23,
     array:["1","2"]
         }
        //实例化
        var otherPerson=object(person);
        otherPerson.array.push("3")
        alert(otherPerson.name);
        alert(otherPerson.age);//继承了age属性
        alert(person.array);//1,2,3  会改变原对象

子对象的继承属性改变,也会改变父对象的属性值。

然而ECMAScript5通过新增Object.create()函数规范了原型式继承,这个函数接受两个参数,第一个参数一个用作新对象的对象(父对象),第二个新对象的额外属性

demo:

 var person={
         name:"name",
         age:23,
     array:["1","2"]
         }
        //实例化
        var otherPerson=Object.create(person);
        otherPerson.array.push("3")
        alert(otherPerson.name);
        alert(otherPerson.age);//继承了age属性
        alert(person.array);//1,2,3  会改变原对象
        otherPerson

支持Object.create()方法的浏览器有IE9+,FF4+,S5+,O12+,Chrome。

优点:没有?
缺点:只能用于对象,改变子对象的属性会改变付对象的属性。不能继承方法。

寄生式继承:
思想:与原型式继承类似,创建一个仅用于封装继承过程的函数,在该函数的内部通过某种方式来增强对象(也就是添加属性或者方法),最后像真的做了所有工作一样返回对象。
eg:

 function object(o){
     function F(){};
     F.prototype=o;
     return new F();
 }
 function createAnther(original){
    var clone=object(original);//创建新对象
     clone.sayHi= function () {
         alert("hi!")
     };//增强对象(添加了一个方法)
     return clone;//返回对象
 }

这种继承仅适用于对象而不是自定义类型和构造函数。
demo:

 function object(o){
     function F(){};
     F.prototype=o;
     return new F();
 }
 function createAnther(original){
     var clone=object(original);
     clone.sayHi= function () {
         alert("hi!")
     };
     return clone;
 }
 var person={
     name:"name",
     age:24,
     work:"student",
     array:["1","2"]
 }
 var otherPerson=createAnther(person);
 alert(otherPerson.name);
 alert(otherPerson.age);
 alert(otherPerson.work);
 otherPerson.sayHi();
 otherPerson.array.push("3");
 alert(otherPerson.array);//1,2,3   会改变原对象

改变子对象的属性也会改变父对象的属性,而且createAnther函数中的object函数不是必须的,任何返回一个对象的函数都适用于这个模式。

缺点:只能适用对象。



寄生式组合继承:

思想:通过原型链的混成形式来继承方法,通过构造函数来继承属性。基本的思路是,不比为了子类型的原型而调用构造函数,我们需要的无非是父类型的一个副本而已,本质上,就是使用寄生式继承来继承父类型的原型,然后再将结果指定给子类型的原型。
其基本模式如下:

//b继承a
function inhertPrototype(a,b){
     var prototype=object(a.prototype);
            prototype.constructor=b;
            b.prototype=prototype;
 }

先创建父类型的原型的副本,再为创建的副本添加constructor属性,再把这个副本赋给子类型。这样我们就可以用inhertPrototype函数去替换为子类型原型赋值的语句。这样就可以用新创建的副本,避免两次调用父函数。弥补了组合继承的缺点。
demo:

//b继承a
function object(o){
            function F(){};
            F.prototype=o;
            return new F();
        }
function inhertPrototype(a,b){
     var prototype=object(a.prototype);
            prototype.constructor=b;
            b.prototype=prototype;
 }
 function a(){
     this.name="name";
     this.array=["1","2","3"];
     this.weight=268;
 }
 a.prototype.sayName= function () {
     alert(this.name);
 }
 function b(name,array){
 a.call(this,array,name);
     this.age=29;
 }
 inhertPrototype(a,b);
 b.prototype.sayAge=function(){
     alert(this.age);
 }
 var person=new b();
 alert(person.age);//29
 alert(person.name);//name
 person.sayAge();//29
 person.sayName()//name
 //验证不会影响原型属性
 person.array.push("4");
 alert(person.array);//1,2,3,4
 var personTest=new b();
 alert(personTest.array);//1,2,3

寄生式继承的原型链不会改变。被普遍认为是引用类型最理想的继承范式。
优点:完美。
缺点:不知道~



总结:
原型链,构造函数,组合继承,寄生组合式继承都适用于自定义类型,而原型式继承和寄生式继承只适用于对象,与原型链模式一样,改变子对象的属性,父对象的属性值也会改变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值