Javascript高级程序设计--读书笔记之理解原型对象
先上一段代码和关系图
function Person(){} Person.prototype.name = "Nic" Person.prototype.age = 22 Person.prototype.job= "Software Engineer" Person.prototype.sayName = function(){ alert(this.name) } var person1 = new Person(); var person2 = new Person(); alert(person1.sayName == person2.sayName) //true
图1
开始进入正题
无论什么时候,只要创建了一个新的函数,就会根据特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性是一个指向protopype属性所在函数的指针,拿上面的代码来说,Person.prototype.constructor指向Person。通过这个构造函数我们还可以继续为原型对象添加其他属性和方法。
创建自定义的构造函数之后其原型对象只会默认取得constructor属性;至于其他方法都是从Object继承而来的。当调用一个构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型属性ES5中管这个指针叫[[Prototype]]。虽然在脚本中没有标准的方式访问[[Prototype]],但是在Firefox、Safari、Chrome在每个对象上都支持一个属性_proto_;这个连接存在于实例与构造函数的原型对象之间,而不是存在与实例与构造函数之间。图1 就展示了各个对象之间的关系
再来一段代码
function Person(){ } Person.prototype.name = "Nic" Person.prototype.age= 29 var person1 = new Person() var person2 = new Person() person1.name = "Greg" alert(person1.name) //"Greg" 来自实例 alert(person1.name) //"Nic" 来自原型 delete person1.name alert(person1.name) //"Nic" 来自原型
上面这段代码说明了,可以通过对象实例访问到原型里的值,但是不能通过对象实例重写原型的值。如果我们添加了一个属性,而该属性和原型对象的属性重名,我们就在该实例中创建该属性,并且屏蔽原型中的那个属性,可以通过delete删除实例的的属性,从而让我们重新访问原型的属性
我们再再来一段代码
function Person(){ } var friend = new Person() Person.prototype.sayHi = function(){ alert("Hi") } friend.sayHi() //"Hi" (没有问题!) Person.prototype = { constructor:Person, name:"Nic", sayHi:function(){ alert(this.name) } } friend.sayHi() //"Hi" (还是"Hi")
上述代码说明两个问题
1.实例与原型之间的关系是松散的调用firend.sayHi()会先在实例中搜索,没有找到就会去原型中搜索,因为实例和原型之间的连接仅仅是一个指针而不是一个副本,因此可以获得原型中更新后的sayHi属性,
2.如果我们重写整个原型对象那么情况就不一样了。我们知道调用构造函数时会为实例添加一个指向最初原型的指针[[Prototype]],而把原型修改为另一个对象就等于切断了构造函数与最初原型之间的联系
看图
重写原型对象之前
图2
重写原型对象之后
图3