javascript中面向对象的程序设计——继承

2 继承

javascript中的继承只要是依靠原型链实现的。
2.1 原型链
基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法。
是不是有点绕。。。其实很简单,就是让一个类A的原型指向另个类B的实例,A.prototype=new B();那么,A.prototype就有了一个指向B的原型对象的指针(其实就是有了一个属性__proto__,这个属性等于B.prototype),再对B做出同样的动作,那么B就有了一个指向C(另一个类)的原型对象的指针,这样,就把这些类像链子一样串起来,形成了“原型链”。
有这样一个函数inherit,参数p,执行inherit(p),返回一个继承p所有属性的对象,代码如下:
    function inherit(p) {
        if (p == null) {
            throw TypeError();
        }
        if (Object.create) {
            return Object.create(p);
        }
        var t = typeof p;
        if (t !== "Object" && t !== "function") {
            throw TypeError();
        }
        function f() {
        }

        f.prototype = p;
        return new f();
    }
上述代码中最核心的部分为后面三句,定义一个函数f,另f.prototype=p,再返回f的一个实例,那么得到的一个新的对象就继承了p中的所有属性,值得注意的是,如果现在改变继承后结果的属性,原属性不变,实际中可以利用这一点。

原型链继承最核心的一句就是A.prototype=new B(),需要好好理解其中父类和子类中的属性,分别指向谁,这里做一个简单的梳理:
有父类Supper和子类Sub,代码如下:
    function Supper() {
    }
    var supper = new Supper;
    function Sub() {
    }
    Sub.prototype = new Supper();
    var sub = new Sub();
    sub.__proto__ == Sub.prototype//true
    Sub.prototype.__proto__ == Supper.prototype//true
    supper.__proto__ == Supper.prototype//true
    Supper.prototype.constructor == Supper//true
    Supper.prototype.__proto__ == Object.prototype//true
从上述代码的实验中,不难发现其中的指向关系,这其实就是一个“原型链”。
在实际运用中,很少会“单独”使用原型链实现继承,但是上述讲解应该让我们更好的理解“原型链”这一概念。
2.2 借用构造函数
这种实现继承的方式主要是利用到了call或者apply这些东西,原理就是在子类中执行父类的构造函数,同时把子类内部的this指针赋给父类,这样达到了继承的目的,在实际运用中“单独”用的不多。
2.3 组合继承
好吧,实在不理解为什么分得这么细,同时用原型链的方式和借用构造函数的方式就叫组合继承。。。
谢了一个简要的代码如下:
    function Supper() {
    }
    function Sub() {
        Supper.call(this);
    }
    Sub.prototype = new Supper();
    var sub = new Sub();
组合继承的核心代码就是Supper.call(this)和Sub.prototype=new Supper()的同时使用,原来存在的一个问题就是多个Sub类的实例会公用Supper类中的属性,因为他们的属性都是从原型中拿到的,而原型相当于一个父类的实例,那当然是一样的咯,通过上述写法,实际上是给Sub类中添加了Supper类中的属性,之后每个Sub类的实例都有自己的属性啦,就不用从原型中拿了,那当然拿的就不一样了。就是说有两份重复的属性。说这一种用的方式最多。。。
这种继承方式会有一个问题,不难发现的是,在 Sub.prototype = new Supper()中 会执行一遍Supper构造函数,然后每初始化一个子类,都会·执行一遍子类的构造函数,然后再执行一遍父类的构造函数。
2.4 原型式继承
主要用到的就是上面写到的inherit函数,做如下测试:
    var a = {x: 1};
    var b = inherit(a);
    a.__proto__ == b;//true
    b.__proto__ == Object.prototype;//true

    delete b.x;//true
    b.x;//1
    b.x = 3;
    b.x//3
    delete b.x//true;
    b.x//1
做了一个有趣的实验,就是上述代码中后一些代码,可以推测出,delete删除的是一个对象本身的属性,不会删除其原型链的值,而在此给b.x复制的时候不是改变了a的值,而是覆盖了a值。删除之后,又显示了a中的值。
另外需要注意的是如果a中的属性是引用的类型(数组,对象什么的,就是除去基本类型的类型),那么a和b的值会共享。
2.5 寄生式继承
这种继承的方式也是利用大了inherit函数,只不过会在对返回的对象进行进一步的封装,给其添加属性和方法等,在此不再详述。
2.6 寄生组合式继承
因为组合式继承的问题(就是一个实例中有两份属性,一份是自己的,另一份来自于原型链),又出现了这种继承方式。
首先,上一段代码:
    function inheritPrototype(subType,superType){
        var prototype=inherit(superType.prototype);
        prototype.constructor=subType;
        subType.prototype=prototype;
    }
笔者初看这段代码是有点懵逼的。。。
一句一句理解,第一句,取得父类原型的一个副本(先这么理解吧,就是包含了父类原型的所有属性), 实际上就是prototype.__proto__指向于SuperType.prototype,第二句,在将子类赋给prototype的constructor,第三句,将prototype赋给子类的原型。这样就实现了子类和父类的关系。
上代码,看了下面这两句代码之后会有所理解:
    subType.prototype.constructor == subType;
    subType.prototype.__proto__ == superType.prototype;
再写一个实际的例子:
    function Super() {
        this.x = 1;
    }
    function Sub() {
        Super.call(this);
    }
    inheritPrototype(Sub, Super);
    var sub = new Sub();
    sub.__proto__ == Sub.prototype//true
    sub.hasOwnProperty("x");//true;
    Sub.prototype.constructor == Sub;//true
    Sub.prototype.__proto__ == Super.prototype//true
从上述例子中能发现其中的关系,子类Sub的实例sub只有一份属性x,这是从执行一次父类的构造函数给的,其他继承的属性是从父类的原型即Super.prototype拿到的。
据说这是一个理想的继承方式。
2.7 继承的小总结
讲解了几种继承的方式,其实最需要做的就是理解“原型链”这个定义,特别是每一个实例中,都有一个__proto__这个指针,指向其原型对象(这个说是在有的浏览器中没有实现,不过为了更好的理解原型链,可以假装有。。。),再加以理解的是原型中有的两个属性constructor和__proto__,constructor指向该类,__proto__指向该原型链的上一层,最初的时候指向Object.prototype,这也正好证实了所有的对象均继承自Object。再下一章,将会对继承做深一步的研究。






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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值