上次已经详细分析了Person构造函数、Person构造函数的原型对象以及Person的实例对象之间的关系,现在我们来探讨它们的作用域链之间的关系。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| unction Person(name,age,job) { this.name = name; this.age = age; this.job = job; this.sayNationality = function(){ alert(this.nationality); }; } Person.prototype.name = "Ken"; Person.prototype.age = 18; Person.prototype.job = "Student"; Person.prototype.nationality = "China"; Person.prototype.sayName = function () { alert(this.name); } var person1 = new Person('Nicholas',29,'Software Engineer'); person1.sayName(); person1.sayNationality(); var person2 = new Person('Nicholas',29,'Software Engineer'); person2.sayName();
|
每当代码
person1.sayName();//Ken
person1.sayNationality();//China
person2.sayName();//Ken
读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。也就是说,在调用person1.sayName()的时候,会先后执行两次搜索。首先,解析器会问:“实例person1有sayName属性吗?”答:“没有。”然后,它继续搜索,再问:“person1的原型有sayName属性吗?”答:“有。”于是,它就读取那个保存在原型对象中的函数。同理,当调用person1.sayNationality方法的时候,会先在当前实例对象中搜索有没有sayNationality方法,有就调用这个方法,在执行sayNationality方法的时候,实例对象并没有this.nationality属性成员,它就会通过实例对象的prototype属性的作用域链搜索nationality标识符,在person1的原型对象上有nationality属性,于是就可以正常读取它保存的值。当我们调用person2.sayName()时,将会重现相同的搜索过程,得到相同的结果。而这正是多个对象实例共享原型所保存的属性和方法的基本原理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| person1.constructor person1.constructor.prototype person1.constructor.prototype.name */ person1.prototype; 怎样可以不通过person1.constructor来访问prototype呢?可以利用原型对象上的isPrototypeOf()方法来确定person1与原型对象之间是否存在这种关系,若存在就通过Object.getPrototypeOf(person1).name来访问person1的原型对象的成员name属性。 */ if(Person.prototype.isPrototypeOf(person1)) { alert(Object.getPrototypeOf(person1).name) }
|
虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。来看下面的例子。
1 2 3 4 5 6 7 8 9 10 11 12
| function Person(){} Person.prototype.name = "Ken"; Person.prototype.age = 18; Person.prototype.job = "Student"; Person.prototype.sayName = function () { alert(this.name); } var person1 = new Person(); var person2 = new Person(); person1.name = "Greg"; alert(person1.name); alert(person2.name);
|
不过使用delete操作符则完全可以完全删除实例属性,从而让我们能够重新访问原型中的属性,代码如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function Person(){} Person.prototype.name = "Ken"; Person.prototype.age = 18; Person.prototype.job = "Student"; Person.prototype.sayName = function () { alert(this.name); } var person1 = new Person(); var person2 = new Person(); person1.name = "Greg"; alert(person1.name); alert(person2.name); delete person1.name; alert(person1.name);
|
使用hasOwnProperty()方法可以检查一个属性是存在于实例中,还是存在于原型中。这个方法(不要忘了它是从Object继承来的)只在给定属性存在于对象实例时,才会返回true。
1 2
| alert(person1.hasOwnProperty("name"));
|
原型与in操作符
有两种方式使用in操作符:单独使用和在for-in循环中使用。在单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。
1 2
| alert("name" in person1);
|