ITEEDU

javascript面向对象设计-继承

原型链

原型链很强大,可以用它来实现继承,可是又存在一些问题,其中最主要的来自包含引用类型的原型。

包含引用类型值的属性会被所有实例共享;而这也正是为什么要在构造函数中,而不是原型对象中定义属性的原因。

通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了。来看下面的代码:

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

function ZiLei(){
}
//继承ChaoLei
ZiLei.prototype = new ChaoLei();
var instance1 =new ZiLei();
instance1.colors.push("yellow");//red black green yellow
alert(instance1.colors);

var instance2 = new ZiLei();
alert(instance2.colors);//red black green yellow

这段代码ChaoLei构造函数(之前提过,构造函数用大写字母开头)定义了一个colors属性,该属性包含一个数组(引用类型的值)。ChaoLei的每个实例都会各自包含自己数组的colors属性。

当ZiLei通过原型继承了ChaoLei之后,Zilei.prototye就成为了ChaoLei的一个实例,因此他有了自己的colors属性。

结果是:ZiLei的所有实例都共享colors属性(例如:instance1和instance2),所以:对instance1进行修改,从instance2也可以反映出来。

那么如何解决这个问题呢?那就只能用下面这种办法:借用构造函数。

借用构造函数

在子类型构造函数的内部调用超类型构造函数。

这句话不好理解,换句话说要做到这句话的方法是:通过使用call()和apply()方法,在(将来)新创建的对象上执行构造函数,具体做法如下面这个例子:

function ChaoLei(){
    this.colors = ["red","black","green"];
}
function ZiLei(){
    //继承ChaoLei
    ChaoLei.call(this);   
}
var instance1 = new ZiLei();
instance1.colors.push("yellow");
alert(instance1.colors);
var instance2 = new ZiLei();
alert(instance2.colors);

问题:

会将所有父中的对象都添加到子对象中,方法不可复用。

组合继承

用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。来看例子:
function ChaoLei(name){
    this.name = name;
    this.colors = ["red","blue","black"];           
}
ChaoLei.prototype.sayName = function(){
    alert(this.name);
};
function ZiLei(name,age){
    //继承属性
    ChaoLei.call(this,name);
    this.age = age;
}
//继承方法
ZiLei.prototype = new ChaoLei(); 
ZiLei.prototype.sayAge = function(){
    alert(this.age);
};
var instance1 = new ZiLei("Tony",22);
instance1.colors.push("green");
alert(instance1.colors); // "red","blue","black","green"
instance1.sayName();//Tony
instance1.sayAge();//22

var instance2 = new ZiLei("Tom",30);
alert(instance2.colors);// "red","blue","black"
instance2.sayName();//Tom
instance2.sayAge();//30

这个例子有点长,我们一步一步看:

  1. ChaoLei构造函数中定义了两个属性:name和colors
  2. ChaoLei的原型中定义了一个方法:sayName()
  3. ZiLei构造函数中调用ChaoLei构造函数,同时传入了name参数,同时自己定义了一个age属性
  4. ZiLei的原型继承ChaoLei的实例,然后又在该新原型上定义了新方法sayAge()
  5. 这个ZiLei的两个不同实例(instance1和instance2)都用他们自己的属性,也可以使用相同的方法了。