继承

许多OO语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,实现继承则继承实际的方法。由于函数没有签名,在ECMAScript中无法实现接口继承。ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的。实现的本质是重写原型对象,代之以一个新类型的实例。

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

我们没有使用SubType默认提供的原型,而是给它换了一个新原型,这个新原型就是SuperType的实例。于是,新原型不仅具有作为一个SuperType的实例所拥有的全部属性和方法,而且其内部还有一个指针,指向了SupperType的原型。最终:instance指向SubType的原型,SubType的原型又指向SuperType的原型。getSuperValue方法仍然存在于SuperType.prototype中,但property则位于SubType.prototype中。这是因为property是一个实例属性,而getSuperValue则是一个原型方法。

另外,在通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样做就会重写原型链。(因为直接赋在了prototype上,而不是赋在了添加到prototype的某个方法上)。

问题:就像创建对象时遇到的问题一样,包含引用类型值的原型属性会被所有实例所共享,而这也正是为什么要在构造函数中,而不是在原型对象中定义属性的原因。现在,在通过原型来实现继承时,原型实际上会变成另一个类型的实例,于是,原先的实例属性也就变成了现在的原型属性了。

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

    function SubType(){
    }

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

    var instance1 = new SubType();
    instance1.colors.push("black");
    alert(instance1.colors);    //red,blue,green,black

    var instance2 = new SubType();
    alert(instance2.colors);    //red,blue,green,black

2.借用构造函数(伪造对象/经典继承)

基本思想:在子类型构造函数的内部调用超类型构造函数。函数只不过是在特定环境中执行代码的对象,因此通过使用apply()和call()方法也可以在(将来)新创建的对象上执行构造函数。

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

    function SubType(){
        //继承了SuperType
        SuperType.call(this);  //“借调”了超类型的构造函数
    }

    var instance1 = new SubType();
    instance1.colors.push("black");
    alert(instance1.colors);    //red,blue,green,black

    var instance2 = new SubType();
    alert(instance2.colors);    //red,blue,green

我们实际上是在(未来将要)新创建的SubType实例的环境下调用了SuperType构造函数,这样一来,就会在新SubType对象上执行SubType函数中定义的所有对象初始化代码。结果,SubType的每个实例就都会具有自己的colors属性的副本了。

问题:方法都在构造函数中定义,函数复用无从谈起。而且,在超类型的原型中定义的方法对子类型不可见。

3.组合继承(伪经典继承)

思路:使用原型链实现对原型属性和方法的继承,通过借用构造函数实现对实例属性的继承。

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

    SuperType.prototype.sayName=function(){
        alert(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(){
        alert(this.age);
    };

    var instance1 = new SubType("Nicholas",29);
    instance1.colors.push("black");
    alert(instance1.colors);
    instance1.sayName();
    instance1.sayAge();

    var instance2 = new SubType("Greg",27);
    alert(instance2.colors);
    instance2.sayName();
    instance2.sayAge();

 

 

posted @ 2014-04-10 10:27  alexandra  阅读(139)  评论(0编辑  收藏  举报