函数的原型
无论什么时候,只要创建了一个新函数,就会根据js特定的规则为该函数创建一个prototype原型,默认的prototype是一个对象,并且拥有一个constructor属性,该属性指向那个构造函数。
2 | alert( typeof Person.prototype); |
3 | alert(Person.prototype.constructor == Person); |
实例与原型的关系
构造函数的实例有一个隐藏的指向原型的指针(_proto_),但是我们不能通过脚本来访问它,但是这里有个关系:这个指针存在于实例与原型之间,而不是存在于实例与构造函数之间。换句话说,实例与构造函数之间没有直接的关系。
上边这个例子中,虽然Person是个空函数,但是它的实例却有name属性,因为它们的指针都指向prototype,我们可以a.name,a.getName(),
a.copyright来读取属性或方法,那他们到底是怎样的机制呢?
实例搜索原型流程
当代码流执行到实例的对象或方法时,首先会搜索实例对象本身,如果实例对象有对应的属性和方法的时候,就会停止搜索原型对象,如果搜索到实例对象没有相关方法或属性时,就会搜索对应构造函数的原型对象,最终还会搜索到Object.prototype,如果开始到结束都未搜索到相关方法或属性,则undefined,未定义相关属性或方法。
优先级:由搜索过程可知,实例对象自身的属性优先级最高,可决定搜索过程是否中断。
优先级决定了读写问题,给实例添加属性以后,程序就不会再去访问prototype上的相关属性了,所以就起到了原型对象对于实例对象来说是不可写的的机制。
19 | alert(Person.prototype.hasOwnProperty( 'qq' )); |
20 | alert(a.hasOwnProperty( 'qq' )); |
总结一下:
1、实例对象指向原型对象,包括实例.constructor属性也是从原型对象读到的
2、实例对象具有读写优先级,往实例上添加属性或方法后,那么原型对象上的相关属性和方法则被覆盖,这是由搜索机制决定的。
原型的共享
由实例与原型的关系可知,一个构造函数的所有实例都是指向同一个原型的,换句话说:构造函数的所有实例都共享同一个原型。
而每个实例之间都是相互独立的,换句话说:每个实例都是一份拷贝。
通常的做法,把方法的定义都写在原型上,这样可以避免重复定义相同方法。减少内存资源的消耗,当然总会有特殊情况。
凡事都有两面性,共享的好处之外也会引起其他问题
原型模式的问题
那就是引用类型的数据,
2 | Person.prototype.friends = [ 'a' , 'b' ]; |
从这个例子来说,我们改变了一个实例的属性后,却在另一个实例中反映出来了,有些时候我们并不想这样,那我们对于这种引用类型来说,
我们应该给各自的实例特权方法呢,下一节在续。