[JavaScript 学习笔记] 2. 继承


继承

许多 OO 语言支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。如前所述,由于函数没有签名,在 ECMAScript 中无法实现接口继承。 ECMAScript 只支持实现继承,而且其实现继承主要是依靠原型链来实现。

原型链

基本思想是:利用原型让一个引用类型继承另一个引用类型的属性和方法。
构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。

jsfunction SuperType() {
    this.property = true;
}
SuperType.prototype.getSuperValue = function() {
    return this.property;
};

function SubType() {
    this.subproperty = false;
}

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

SuperType.prototype.getSubValue = function() {
    return this.subproperty;
}

var instance = new SubType();
alert(instance.getSuperValue());//true

[JavaScript 学习笔记] 2. 继承

  1. 别忘记默认的原型

    [JavaScript 学习笔记] 2. 继承

  2. 确定原型和实例的关系

    jsalert(instance instanceof Object); //true
    alert(instance instanceof SuperType); //true
    alert(instance instanceof SubType); //true
    
    jsalert(Object.prototype.isPrototypeOf(instance));//ture
    alert(SuperType.prototype.isPrototypeOf(instance));//ture
    alert(SubType.prototype.isPrototypeOf(instance));//ture
    
  3. 谨慎地定义方法

    给原型添加方法的代码一定要放在替换原型的语句之后。

    jsfunction SuperType() {
    this.property = true;
    }
    
    SuperType.protype.getSuperValue = function() {
    return this.property;
    }
    
    function SubType() {
    this.subproperty = false;
    }
    //继承了 SuperType
    SubType.prototype = new SuperType();
    //添加新方法
    SubType.prototype.getSubValue = function() {
    return this.subproperty;
    };
    //重写超类型中的方法
    SubType.prototype.getSuperValue = function() {
    return false;
    };
    
    var instance = new SubType();
    
    alert(instance.getSuperValue());//false
    
    jsfunction 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;
    },
    someOtherMethod = function() {
        return false;
    }
    };
    
    var instance = new SubType();
    alert(instance.getSuperValue()); //error 原型链被切断(现在的原型包含的是 Object 的实例)。
    

4.原型链的问题

最主要的问题来自包含引用类型值的原型。前面已经介绍过包含引用类型值的原型属性会被所有实例共享,这也是为什么要在构造函数中而不在原型对象中定义属性的原因。
第二个问题是:在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给炒类型的构造函数传递参数。

借用构造函数(伪造对象或经典继承)

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

jsfunction 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"

传递参数

js    function SuperType(name) {
        this.name = name;
    }

    function SubType() {
        //继承了 SuperType,同时还传递了参数
        SuperType.call(this,"PaddingMe");

        //实例属性
        this.age = 25;
    }

    var instance = new SubType();

    alert(instance.name); //"PaddingMe"
    alert(instance.age); //25

组合继承(伪经典继承)

即将原型链和借用构造函数的方法组合在一起,思路为使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。

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

    SuperType.prototype.sayName = function() {
        alert(this.name);
    }

    function SubType() {
        //继承属性
        SuperType.call(this,name);

        this.age = age;
    }

    SubType.prototype = new SuperType();
    SubType.prototype.constuctor = SubType();
    SubType.prototype.sayAge = function() {
        alert(this.age);
    }

    var instance1 = new("PaddingMe",25);
    instance1.colors.push("black");
    alert(instance1.colors); //"red,blue,green,black"
    instance1.sayName; //"PaddingMe"
    instance1.sayAge; //25


    var instance2 = new("hw",26);
    alert(instance2.colors); //"red,blue,green"
    instance2.sayName; //"hw"
    instance2.sayAge; //26

原型式继承

js    function object(o) {
        function F(){}
        F.prototype = o;
        return new F();
    }
    var person = {
        name : "PaddingMe";
        friends :["hw","wjj","hz"];
    }

    var antherPerson = object(person);
    antherPerson.name = "Hhb";
    antherPerson.friends.push("zxp");

    var yetAntherPerson = object(person);
    yetAntherPerson.name = "Linda";
    yetAntherPerson.friends.push("him");

    alert(person.friends)//"hw,wjj,hz,zxp,him"

ECMAScirpt 5 中新增 Object.create() 方法规范化了原型式继承。有两个参数,一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。

在传入一个参数情况下,Objetc.create() 和 object() 方法的行为相同。

js    var person = {
        name : "PaddingMe";
        friends :["hw","wjj","hz"];
    }
    var antherPerson = Object.create(person);
    antherPerson.name = "Hhb";
    antherPerson.friends.push("zxp");

    var yetAntherPerson = Object.create(person);
    yetAntherPerson.name = "Linda";
    yetAntherPerson.friends.push("him");

    alert(person.friends)//"hw,wjj,hz,zxp,him"

第二个参数与 `Object.defineProperties() 方法的第二个参数格式相同: 每个属性都是通过自己的描述符定义的。

js    var person = {
        name : "PaddingMe";
        friends :["hw","wjj","hz"];
    }

    var anthorPerson = Object.create(person, {
        name: {
            value:"hehe";
        }
    })

    alert(anthorPerson.name);//"hehe"

寄生式继承

即创建一个仅用于封装继承构成的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。

js    function createAnother(original) {
        var clone = object(original); //通过调用函数创建一个新对象
        clone.sayHi = function() { //以某种方式来增强这个对象
            alert("hi");
        };
        return clone; //返回这个对象
    }

    var person = {
        name : "PaddingMe";
        friends :["hw","wjj","hz"];
    }

    var anthorPerson = createAnother(person);
    anthorPerson.sayHi();//"hi"

寄生组合式继承

jsfunction 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); //第二次调用SuperType();

    this.age = age;
}

SubType.prototype = new SuperType(); // 第一次调用SuperType();

SubType.prototype.constuctor = SubType;

SubType.prototype.sayAge = function(){
    alert(this.age);
}

[JavaScript 学习笔记] 2. 继承

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

js    function inheritPrototype(subType,superType){
        var prototype = object(superType.prototype); //创建对象
        prototype.constructor = subType; //增强对象
        subType.prototype = prototype; // 指定对象
    }
jsfunction 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); //第二次调用SuperType();

    this.age = age;
}

inheritPrototype(SubType,SuperType);

SubType.prototype.sayAge = function(){
    alert(this.age);
}

7 responses on “[JavaScript 学习笔记] 2. 继承

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>