要讲JS的原型模式的修改与重写的话,这里不得不提一句原型模式的理解,我个人认为只要清楚对象实例、构造函数和函数原型的关系就可以了。举一个例子:
function Cat(){
}
Cat.prototype.type="cat";
Cat.prototype.eat=function(){
alert("我爱吃鱼");
} //为构造函数的原型添加属性
var cat1 = new Cat(); //创建对象实例
cat1.eat(); //实例继承原型的属性
【在每个实例对象中,都有一个[[prototype]]指针指向实例对象的构造函数的原型;每个构造函数中都有一个prototype的指针指向该构造函数的原型;而原型则有一个constructor指针,指向他的构造函数。】
上述代码中构造函数为Cat(),Cat的原型Cat.prototype指向Cat的原型,原型有type和eat两个属性,cat1为构造函数Cat()的实例对象,继承了Cat的原型的所有属性。解析图如下:
另外多说一句,在访问对象实例的属性时,会从对象实例本身开始找,如果没有就一直向上访问直到找到或者无法继续向上查找为止,所以上述例子中的cat1可以向上访问到原型中的属性。
如大家所知的,原型模式有两种写法,一种为给原型添加属性,而另一种就是重写原型。
原型模式的修改:
function Cat(){
}
Cat.prototype.type="cat"; //为原形添加属性type
Cat.prototype.eat=function(){
alert("我爱吃鱼");
} //为原型添加属性eat
var cat1 = new Cat(); //创建对象实例
cat1.eat(); //实例继承原型的属性
向上述写法就是向原型添加属性的写法,它的初始关系图如下:
如果我们在创建好cat1之后再去为原型添加些属性,比如:
function Cat(){
}
Cat.prototype.type="cat";
Cat.prototype.eat=function(){
alert("我爱吃鱼");
} //为构造函数的原型添加属性
var cat1 = new Cat(); //创建对象实例
cat1.eat(); //我爱吃鱼
Cat.prototype.name="fit cat"; //给原型添加name属性
Cat.prototype.eat=function(){ //修改原型的eat属性
alert("我变了,我不爱吃鱼了!");
}
cat1.eat(); //我变了,我不爱吃鱼了!
alert(cat1.name); //fit cat
我们会发现,cat1仍旧继承了原型的name属性,很好理解,因为在使用上述向原型添加属性时,毕竟是添加,原型还是原来的原型,对象实例cat1的[[prototype]]指针没有改变指向,所以和之前的一样,可以向上访问到name属性。此时的关系图如下:
原型模式的重写
而如果上述代码变成如下:
function Cat(){
}
Cat.prototype.type="cat";
Cat.prototype.eat=function(){
alert("我爱吃鱼");
}
var cat1 = new Cat();
cat1.eat();
Cat.prototype={ //重写Cat的prototype
name:"fit cat",
construct:Cat
//指明新原型的构造函数指针指向Cat,如果没有指明,则指向Object()
};
alert(cat1.name); //undefinded
cat1.eat(); //我爱吃鱼
var cat2 = new Cat(); //以新的原型创建一个新的对象实例
alert(cat2.name);
//由于新的原型中有name属性,所以alert为fit cat
alert(Object.getPrototypeOf(cat1)==Object.getPrototypeOf(cat2)); //false,即说明cat1和cat2的原型指针所指不一样
cat2.eat();
//由于新的原型中没有eat属性,所以报错。
可以发现,Cat.prototype={…};重写了Cat.prototype指针,可以理解为这一句代码将Cat的prototype指针从之前的原型指向了一个新的原型。又因为我们的实例cat1的原型中没有name属性,所以向上访问不到,所以alert为undefined,而cat2是构造函数以新的原型构造出的新的对象实例,它的原型中有name属性,所以alert为fit cat,而由于它的原型中没有eat属性,所以报错。具体关系图如下:
总结下来就是下面两句话:
“实例中的指针仅指向原型,而不是指向构造函数”。
“重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的关系;它们引用的仍然是最初的原型”。