由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来,即使是先创建了实例后修改原型也照样如此。如下示例:
var friend = new Person(); Person.prototype.sayHi = function() { alert("hi") ; }; friend.sayHi(); // "hi"
以上代码先创建了Person的一个实例, 并将其保存在friend中。然后,下一条语句在Person.prototype中添加了一个方法sayHi()。即使friend实例是在添加新方法之前创建的,但它仍然可以访问这个新方法。其原因可以归结为实例与原型之间的松散连接关系。当我们调用friend.sayHi()时,首先会在实例中搜索名为sayHi 的属性,在没找到的情况下,会继续搜索原型。因为实例与原型之间的连接只不过是一个指针, 而非一个副本, 因此就可以在原型中找到新的sayHi属性并返回保存在那里的函数。
尽管可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但如果是重写整个原型对象,那么情况就不一样了。我们知道, 调用构造函数时会为实例添加一个指向最初原型的[[Prototype]]指针, 而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。请记住:实例中的指针仅指向原型,而不指向构造函数。看下面的例子:
function Person(){} var friend = new Person(); Person.prototype = { constructor: Person, name : "Nicholas" , age : 29, job : "Software Engineer" , sayName : function () { alert (this .name); } }; friend.sayName(); //error
在这个例子中,先创建了Person 的一个实例,然后又重写了其原型对象。然后在调用friend.sayName()时发生了错误,因为friend指向的原型中不包含以该名字命名的属性。原因看下图:
根据上图中重写原型对象前后的对比可以看出,重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系;它们引用的仍是最初的原型。