浅谈js的几种模式(二)
好了,继续。废话不多说直接开始。
既然讲到了原型模式,那首先就要弄明白什么是原型对象。无论什么时候,创建一个函数,就会自动为该函数生成一个prototype属性指向函数的原型对象。而在默认情况下,所有原型对象都会自动获得一个constructor属性,该属性包含一个指向prototype属性所在函数的指针。
function Person () { } Person.prototype.name="DJL"; Person.prototype.age=22; Person.prototype.job="student"; Person.prototype.sayName=function(){ alert(this.name); } var person1= new Person (); person1.sayName(); var person2= new Person (); person2.sayName(); alert(person1.sayName==person2.sayName);//true
就拿上面的的代码来举个例子
Person.prototype指向了原型对象,而Person.prototype.constructor又指向了构造函数Person,原型对象中除了包含自动获得的constructor属性外,还有用户自定义的其他属性(name,job,age等等)。而实例person1和person2中都包含一个内部属性,来指向Person.prototype。也就是说实例与构造函数之间并没有直接的联系。说的太抽象来张图片,一看便知。
虽然我们没有办法访问到[[Prototype]]这个属性,但是我们可以通过isPrototypeof这个函数来确定对象之间是否存在某种关系,例:
alert(Person.prototype.isPrototypeOf(person1));//true alert(Person.prototype.isPrototypeOf(person2));//true
当然ES5提供了一个方法用来方便的得到原型对象。Object.getPrototypeOf();
alert(Object.getPrototypeOf(person1)==Person.prototype)//true
但是目前支持这个方法的只有IE9+,Firefox 3.5+,Safari 5+,Opera12+,chrome这些浏览器。
当对象实例去查找某个属性时,都会执行一次搜索,首先会在这个实例本身搜索是否包含同名的属性,如果搜索到了,则返回这个属性值(即使原型对象中也有这个属性,也不会再到原型对象中进行搜索),如果没有,会继续在其原型对象中进行搜索,如果搜索到,则返回这个属性值。所以我们可以通过对象实例去访问保存在原型中的属性,但是不能通过对象实例重写原型中的属性值,因为当对象实例中添加一个与原型对象中同名的属性,这个属性就会屏蔽保存在原型对象中的同名属性。到这里,我们就需要知道这个属性到底是保存在对象实例中还是在原型对象中。我们使用hasOwnPrototype来进行检测。
function Person () { } Person.prototype.name="DJL"; Person.prototype.age=22; Person.prototype.job="student"; Person.prototype.sayName=function(){ alert(this.name); } var person1= new Person (); person1.sex="boy"; person1.sayName(); var person2= new Person (); person2.sayName(); alert(person1.hasOwnProperty("sex"));//true alert(person1.hasOwnProperty("name"));//false
原型与in操作符
有两种方式使用in:一种是直接使用in另一种是在for循环中使用in。单独操作时,in操作符通过对象能够访问到属性则会返回true,否则false。这里不管属性存在在对象实例中还是在原型对象中,只要有这个属性都会返回true。
alert("name" in person1);//true alert("sex" in person1);//true
那么就可以利用hasOwnProperty方法和in操作符来判断某个属性是不是只存在与原型中,例:
function hasPrototypeProperty (object ,attr) { return !object.hasOwnProperty(attr) && (attr in object); }
在for-in里面循环时,返回所有能够通过对象访问的,可枚举的属性,其中既包括了对象实例中的属性,也包括了原型对象中的属性。
在上面的实例中我们发现,每个都要写成如下形式
Person.prototype.name="DJL"; Person.prototype.age=22; Person.prototype.job="student";
如果有很多原型属性则会很麻烦,可以改为如下形式
Person.prototype={ name:"DJL", age:22, job:"student" }
即用对象字面量的形式来代替,但是这种方式会改变原型对象中constructor属性,因为这种方式的实质是重写了对象原型。那么此时的constructor将不再指向Person而是Object。
alert(Person.prototype.constructor instanceof Object)//true
所以我们可以手动更改过来
Person.prototype={ name:"DJL", age:22, job:"student",
constructor:Person,
}
接下来我们看看这个
function Person () { } Person.prototype={ constructor:Person, name:"DJL", age:22, job:"student", sayName:function(){ alert(this.name); } } var person1= new Person (); person1.sayName(); var person2= new Person (); person2.sayName(); //alert(person1.sayName==person2.sayName); alert(Person.prototype.constructor instanceof Object); person1.sayName();//error
我们会发现执行person1.sayName();//error 报错了 ,为什么呢,我们已经知道,这是对象在调用属性,首先在对象实例中查找,如果没有就在原型对象去查找,显示实例对象中是没有这个属性的,那么原型对象中是否有呢,我们直接看图吧,一目了然。
我们可以看到当重写了prototype后,就切断了构造函数和最初原型之间的关系,而实例对象中的[[Prototype]]还是指向最初的原型,而最初原型中没有sayName()这个属性,所以执行person1.sayName();//error 会报错。相信报错的原因都懂了吧,懂了吧,懂了吧。
好了,这篇就先写到这里吧,还有第三篇哦。