原型链的问题
原型链的作用很强大,可以用来实现继承,但是也会有一些问题,最大的问题就是引用类型值得原型属性会被所有实例共享
实例是什么? 实例就是 new 对象名称() ; 例如: var instance=new Person(); instance就是实例;
例如:
var SuperType=function(){ this.colors=["red","black"]; } var SubType=function(){}; SubType.prototype=new SuperType();//继承 var subType=new SubType(); subType.colors.push("green"); console.log(subType.colors); var subType2=new SubType(); console.log(subType2.colors);
这段代码输出结果都是:"red","black","green"
这是怎么回事了?
这是因为SubType的所有实例都会共享colors属性,而我们对subType.colors的修改,能够通过subtype2.colors反映出来。
借用构造函数
在解决原型中引用类型值得问题中,可以通过借用构造函数的技术来实现;即:就是通过子类型的构造函数调用父类的构造函数(可以通过call或者apply调用父类构造函数)
以上面的例子改成如下:
var SuperType=function(){ this.colors=["red","black"]; } var SubType=function(){ SuperType.call(this) //调用父类的构造函数 }; SubType.prototype=new SuperType();//继承 var subType=new SubType(); subType.colors.push("green"); console.log(subType.colors); var subType2=new SubType(); console.log(subType2.colors);
输出的结果为:"red","black","green"
"red","black"
这样我们实际是在(未来将要)新创建的SubType实例环境下调用了SupType构造函数
这样就会在新的SubType对象上执行SuperType函数,而SubType的每个实例都会具有colors属性的副本;
最基本的原型继承
var Parent=function(name){ this.name=name || "parent"; } Parent.prototype.getName=function(){ return this.name; } var Child=function(name,age){ this.name=name; this.age=age; } Child.prototype=new Parent();//通过此种方法继承 var parent=new Parent("this is a parent"); var child=new Child("I‘m a child",22); console.log(parent.getName()); //this is a parent console.log(child.getName());//I‘m a child console.log(child.age);//22
这种优点是简单,缺点是如果子类需要做跟父类构造函数中相同的初始化动作,需要在子类的构造函数中再重新做一遍,例如:Child中初始化name
如果初始化工作不断增加,这种方式很不方便;
这时候可以借用构造函数,例如:
var Parent=function(name){ this.name=name || "parent"; } Parent.prototype.getName=function(){ return this.name; } Parent.prototype.obj={a:1}; var Child=function(name,age){ //通过apply调用父类构造函数进行初始化工作 //这样不管父类执行多少初始化工作,子类也可以执行同样的初始化工作 Parent.apply(this,arguments); this.age=age; } Child.prototype=new Parent(); //Child.prototype=Parent.prototype; var child=new Child("alice",22); var parent=new Parent("stone"); console.log(child.getName()); console.log(parent.getName());
这种方式可以解决初始化的问题,但是父类构造函数会被执行两次,一次在子类构造函数中,一次是在赋值子类原型链中,
可以通过
Child.prototype=Parent.prototype;//代替 Child.prototype=new Parent();
即子类和父类指向相同的原型链,但是这样会存在一个问题,修改子类的原型链,会影响父类的原型链,例如:
var Parent=function(name){ this.name=name || "parent"; } Parent.prototype.getName=function(){ return this.name; } Parent.prototype.obj={a:1}; var Child=function(name,age){ //通过apply调用父类构造函数进行初始化工作 //这样不管父类执行多少初始化工作,子类也可以执行同样的初始化工作 Parent.apply(this,arguments); this.age=age; } Child.prototype=new Parent(); //Child.prototype=Parent.prototype; var child=new Child("alice",22); var parent=new Parent("stone"); console.log(child.getName()); console.log(parent.getName()); Child.prototype.obj.a=26; console.log(child.obj.a);//26 console.log(parent.obj.a);//26
当我改变 Child.prototype.obj.a时,会同时改变父类的原型链。
那又什么好的办法?