只要创建一个新函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor属性。这个属性包含一个指向prototype属性所在函数的指针。Person.prototype.constructor指向Person。
当调用构造函数创建一个实例,实例的内部将包含指向构造函数的原型对象的指针(内部属性),称为[[Prototype]]。在Firefox、Safari和Chrome通过_proto_访问。这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。
下图展示了各个对象之间的关系:
Person.prototype指向了原型对象,而Person.prototype.constructor又指回了Person。原型中除了constructor属性,还有其他添加的属性。Person实例中都包含一个内部属性,该属性仅仅指向了Person.prototype,它们和构造函数没有直接关系。
虽然无法访问[[Prototype]],但是可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。
alert(Person.prototype.isPrototypeOf(person1)); // true alert(Person.prototype.isPrototypeOf(person2)); // true
在读取某个对象的属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。搜索首先从对象实例本身出发开始,如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找给定名字的属性。如果在原型对象中找到了这个属性,则返回属性的值。
可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果在实例中添加一个与实例原型中的一个属性同名的属性,该属性将会屏蔽原型中的属性。
function Person() { } Person.prototype.name = "xxyh"; Person.prototype.age = "20"; Person.prototype.job = "programmer"; Person.prototype.sayName = function () { alert(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name = "oooo"; alert(person1.name); // "oooo" alert(person2.name); // "xxyh"
上例中,person1中的name属性屏蔽了原型中的name属性。
当对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性。这也就是说,这个属性的存在会阻止对原型中那个属性的访问。使用delete可以完成删除实例属性。
function Person() { } Person.prototype.name = "xxyh"; Person.prototype.age = "20"; Person.prototype.job = "programmer"; Person.prototype.sayName = function () { alert(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name = "oooo"; alert(person1.name); // "oooo" alert(person2.name); // "xxyh" delete person1.name; alert(person1.name); // "xxyh"
hasOwnProperty()可以检测一个属性是存在于实例中,还是存在于原型中。
function Person() { } Person.prototype.name = "xxyh"; Person.prototype.age = "20"; Person.prototype.job = "programmer"; Person.prototype.sayName = function () { alert(this.name); }; var person1 = new Person(); var person2 = new Person(); alert(person1.hasOwnProperty("name")); // false person1.name = "oooo"; alert(person1.hasOwnProperty("name")); // true
下图展示了不同情况的实现与原型的关系:
3.2原型与in操作符
使用in操作符的方式:单独使用、在for-in循环中使用。在单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。
function Person() { } Person.prototype.name = "xxyh"; Person.prototype.age = "20"; Person.prototype.job = "programmer"; Person.prototype.sayName = function () { alert(this.name); }; var person1 = new Person(); alert("name" in person1); // true person1.name = "oooo"; alert("name" in person1); // true
结合前面的hasOwnProperty()特点,可以确定某个属性是原型中的属性还是实例中的属性。如果in操作符返回true而hasOwnProperty返回false,则属性是原型中的属性。
function hasPrototypeProperty(object, name) { return !object.hasOwnProperty(name)&& (name in object); }
接下来,看看hasPrototypeProperty()的用法:
function Person() { } Person.prototype.name = "xxyh"; Person.prototype.age = "20"; Person.prototype.job = "programmer"; Person.prototype.sayName = function () { alert(this.name); }; var person = new Person(); alert(hasPrototypeProperty(person, "name")); // true person.name = "oooo"; alert(hasPrototypeProperty(person, "name")); // false