原型链很强大,可以用它来实现继承,可是又存在一些问题,其中最主要的来自包含引用类型的原型。
包含引用类型值的属性会被所有实例共享;而这也正是为什么要在构造函数中,而不是原型对象中定义属性的原因。
通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了。来看下面的代码:
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
这个例子有点长,我们一步一步看: