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