继承

ECMAScript只支持实现继承,其实现继承主要是依靠原型链来实现的
我们来简单回忆一下构造函数,原型和实例的关系
1.只要创建一个新函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象;
2.所有原型对象都会自动获得一个constructor属性,这个属性是一个指向prototype属性所在函数的指针;
3.每个实例都包含一个指向原型对象的指针。
上述关系如图所示
在这里插入图片描述
然后现在我们让原型对象等于另一个类型的实例,然后层层递进,也就构成了实例与原型的链条,也就是我们一开始所说的原型链。
在这里插入图片描述

        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();
        alert(instance.getSuperValue());

我们定义了两个类型SubType和SuperType,现在我们让SubType继承SuperType,是通过创建后者的实例,并将此实例赋给前者的原型,通过此步骤前者将包含后者实例中的所有属性和方法,并且还有一个指向SuperType原型对象的指针。
所以说,我们并没有使用SubType的默认原型,而是用SuperType的实例作为了他的新原型,到这里我们就得出了继承实现的本质,即重写原型对象,代之以一个新类型的实例。
需要注意的是,instance.constructor指向的是SuperType,因为SubType.prototype中的constructor被重写了。

原型链的问题:
1.原型对象上引用类型的值可以通过实例进行修改,致使所有实例共享着的该引用类型的值也会随之改变。
2.其次是在创建子类型实例时,不能向超类型的构造函数中传递参数。

在解决采用原型链继承带来问题的过程中,开发人员开始使用一种叫做借用构造函数的技术,这种技术的基本思想相当简单,—即在子类构造函数的内部
调用超类构造函数。


    function Person () {
      this.head = 'head';
      this.emotion = ['喜', '怒', '哀', '乐'];
    }
    function Student(studentID) {
      this.studentID = studentID;
      Person.call(this);
    }
    var stu1 = new Student(1001);
    console.log(stu1.emotion); //['喜', '怒', '哀', '乐']

    stu1.emotion.push('愁');
    console.log(stu1.emotion); //["喜", "怒", "哀", "乐", "愁"]
    
    var stu2 = new Student(1002);
    console.log(stu2.emotion); //["喜", "怒", "哀", "乐"]

实现原理:在子类的构造函数中,通过 apply ( ) 或 call ( )的形式,调用父类构造函数,以实现继承。
关于this的指向
(1)在 stu1 = new Student ( ) 构造函数时,是 stu1 调用 Student 方法,所以其内部 this 的值指向的是 stu1, 所以 Person.call ( this ) 就相当于Person.call ( stu1 ),就相当于 stu1.Person( )。
(2)stu1 去调用 Person 方法时,Person 内部的 this 指向就指向了 stu1。那么Person 内部this 上的所有属性和方法,都被拷贝到了 stu1 上。stu2 也是同理。
总之,每个实例都具有自己的 emotion 属性副本。他们互不影响。在子类函数中,通过call ( ) 方法调用父类函数后,子类实例 stu1, 可以访问到 Student 构造函数和 Person 构造函数里的所有属性和方法。这样就实现了子类向父类的继承,而且还解决了原型对象上对引用类型值的误修改操作。

借用构造函数的问题
1.如果仅仅借用构造函数,那么将无法避免构造函数模式存在的问题:方法都在构造函数中定义,因此函数复用就无从谈起。
2.在超类型的原型中定义的方法,对子类而言也是不可见的,结果所有类型都只能使用构造函数模式。

总结

利用原型链的特性和借用构造函数(call、apply)均能实现继承,二者都各有优势和不足。
原型链虽然强大,但存在一些问题,最主要的问题来自包含引用类型值得原型;其次是在创建子类型实例时,不能向超类型的构造函数中传递参数。
而在解决原型中包含引用类型值所带来问题的过程中,开发人员开始使用一种叫做借用构造函数的技术,这种技术的基本思想相当简单,—即在子类构造函数的内部
调用超类构造函数。同时,相对于原型而言,借用构造函数有还有一个很大的优势,它可以在子类构造函数中向超类型构造函数传递参数。
但是,借用构造函数无法避免构造函数模式存在的问题,—即方法都在构造函数中定义,函数复用就无从谈起了。
所以最好的办法是采用组合继承,发挥各自的优势。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值