原型模式+构造函数应该是javascript编辑时使用的非常多的功能。
例如:
function Tang(name,age){
this.name=name;
this.age=age;
this.init();
}
Tang.prototype={
init:function(){
},
alertName:function(){
alert(this.name);
}
}
上面这个原型Tang.prototype={}就是字面量的表示方法。
注意当我们使用字面量表示时,本质上完全重写了默认的prototype对象,因此constructor属性也就变成了新的constructor属性(指向Object构造函数),不在指向Tang函数。
如果要让constructor指向Tang函数,可以强制指向:
Tang.prototype={
constructor:Tang,//强制执行Tang
init:function(){
},
alertName:function(){
alert(this.name);
}
}
字面量方式为什么constructor会指向Object?
因为Tang.protorype={},这种写法其实就是创建了一个新对象,而每创建一个函数,就会同时创建它的prototype,这个对象也会自动获取constructor属性。所有,新对象的constructor重写了Tang原来的constructor,因此会指向新对象,那个新对象没有指向构造函数,那么就会默认为Object.
理解原型:
只要创建一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性。在默认的情况下,所有prototype属性都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。
当用构造函数创建一个新实例后,该实例的内部将包含一个指针,指向构造函数的原型属性。
这个连接存在于实例与构造函数的原型属性之间,而不是存在于实例和构造函数之间。换句话说,他们的内部属性和构造函数没有直接关系,该属性仅仅指向了prototype。
当我们需要给原型添加属性时,需要使用如下方法:
Tang.prototype.sayHi=function(){
alert("hi");
}
而不能用字面量形式:
下面一个例子:
function Tang(){
}
Tang.prototype={
constructor:Tang,
name:"guang"
}
var tang=new Tang();
Tang.prototype={
constructor:Tang,
name:"tang"
}
alert(tang.name);//guang
这里要注意var tang=new Tang();的位置,在申明以后修改的原型是不起作用的
我们稍微修改下位置:
function Tang(){
}
Tang.prototype={
constructor:Tang,
name:"guang"
}
Tang.prototype={
constructor:Tang,
name:"tang"
}
var tang=new Tang();
alert(tang.name);//tang
在申明以前修改的原型就有效果了,可以通俗的理解为,申明完之后,字面量形式就无法修改原型属性了
我们先创建了Tang的实例,然后重写了原型对象,然后调用Tang.name()发生了错误,因为tang指向的原型中不包含以该名字命名的属性。重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系,他们引用的仍然是最初的原型。
原因:调用构造函数时会为实例添加一个指向最初原型的_proto_指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系,实例中的指针仅指向原型,而不指向构造函数。
当代码读取对象的某个属性时,都会执行一次搜索,搜索首先从对象实例本身开始,如果在实例中找到了具有给定名字的属性,就返回,如果没有就搜索对象原型里面的属性。
可以通过对象实例访问保持在原型中的值,但是不能通过对象实例重写原型中的值。
为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性,使用delete操作符可以完全删除实例属性。
还有一个有点麻烦的问题:
上面我们能看的一句话:可以通过对象实例访问保持在原型中的值,但却不能通过对象实例重写原型中的值。
然后我们会在了解原型中遇到一个问题,就是共享的问题,也就是a实例修改了,也会影响b实例
那么上面两句话是否矛盾呢,到底原型中的内容能不能被修改了?
答案是上面两句话都对,原型中的基本值属性不能被修改,而对于引用类型值的属性则是可以修改了