接上一节的创建对象的模式:
原型模式:
对于prototype的理解:我们创建的函数都有一个prototype(原型)属性,这个属性是一个指针指向一个对象,而这个对象的用途是包含基于这个方法的
所有的实例的共享属性和方法。并且每个原型对象都会默认的自动获取一个constructor(构造函数)的属性。这个属性包含一个执行这个prototype属性所在函数的指针。
function Person(){ } Person.prototype.name = 'kim'; Person.prototype.age = 10; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); person1.sayName(); var person2 = new Person(); person2.sayName();
当我们在实例中定义一个和prototype中已有属性重名的属性的时候,该属性会屏蔽掉prototype中的这个属性。而不会修改prototype中的这个属性。
person1.name = 'tom';
alert(person1.name);//tom而不是kim
如果我们在这种情况下依然放问到prototype中的属性,我们可以使用delete操作符来删除实例中的这个属性。
检测属性在实例中还是原型中的方法有:person2.hasOwnPrototype('name');//false只会在当属性是在实例中定义了才会返回true。
in:操作符:无论属性是在prototype还是实例中,只要这个实例具有这个属性就会返回true;
alert('name' in person2);//true
person2.name = 'jimmie';
alert('name' in person2);//true
delete person2.name;
alert('name' in person2);//true
---------综合上面的两种方法就可以确定一个属性到底是在实例中还是原型中
function hasPrototypeProperty(object, name){
return !object.hasOwnPrototype(name) &&(name in object);//true-->在原型中;false-->在实例中
}
Object.keys(对象实例);返回可枚举的属性Object.getOwnPrototypeNames(Person.prototype);//属性不论是否可枚举都会返回。这两个方法都可以替代
for-in来确定属性是否可以被实例方法到。
我们每次在用实例访问属性的时候都会逐个在实例和prototype中查找这个属性,访问顺序是先在实例中查找指定的属性如果找到就会直接返回(所以当实例中
定义了和prototype中相同的属性和方法的时候,实例中的属性就会阻断与prototype中属性的访问)然后才逐个访问prototype中的属性(所以当我们在prototype
中更新了或者新建了一个属性,当实例访问时就可以立即放问到) 但是当我们重写了这个实例的原型就会出现意想不到的结果:
function Person(){ } var person1 = new Person(); Person.prototype={ name: 'tom', sayName: function(){ alert(this.name); } }; person1.sayName();//出现错误
当我们调用构造函数初始化一个实例的时候,会自动的产生一个指针([[Prototype]]),而这个指针指向的最初的原型,而后来我们把原型对象彻底重写以后,就相当于
切断了构造函数与最初原型之间的联系。所以必须要记住:实例中的指针指向的是原型对象,而不是构造函数
原型模式最主要的缺点:创建的所有的属性和方法都是所有实例所共享的。所以当我们需要一些每个实例都具有的特定的属性和方法的时候,原型模式就不是太合适了。