访问对象的构造函数
在原型对象里面有一个constructor属性,该属性指向了构造函数。由于实例对象可以访问原型对象的属性和方法,所以通过实例对象的constructor属性就可以访问实例对象的构造函数。下面通过代码进行演示。
1 function Person() {}
2 // 通过原型对象访问构造函数
3 console.log(Person.prototype.constructor === Person); // 输出结果:true
4 // 通过实例对象访问构造函数
5 var p1 = new Person();
6 console.log(p1.constructor === Person); // 输出结果:true
需要注意的是,如果将构造函数的原型对象修改为另一个不同的对象,就无法使用constructor属性访问原来的构造函数了,示例代码如下。
1 function Person() {}
2 // 修改原型对象为一个新的对象
3 Person.prototype = {
4 sayHello: function() {
5 console.log('hello');
6 }
7 };
8 var p1 = new Person();
9 // 使用实例对象p1可以访问新的原型对象中的属性
10 p1.sayHello(); // 输出结果:hello
11 // 使用constructor属性无法访问原来的构造函数
12 console.log(p1.constructor); // 输出结果:Object() { [native code] }
从上述代码可以看出,p1.constructor的访问结果是Object构造函数,而不是p1原本的构造函数Person。之所以会出现这个效果,是因为第3行为Person.prototype赋值了一个新的字面量对象,这个字面量对象的constructor属性指向的就是Object构造函数,所以p1使用constructor属性访问到的就是Object构造函数了。
为了能在修改了原型对象的情况下仍然能通过constructor属性访问正确的构造函数,我们可以在新的原型对象中将constructor属性指向Person构造函数。示例代码如下。
1 function Person() {}
2 Person.prototype = {
3 constructor: Person, // 手动指向Person构造函数
4 sayHello: function() {
5 console.log('hello');
6 }
7 };
8 var p1 = new Person();
9 console.log(p1.constructor === Person); // 输出结果:true
在上述代码中,由于新的原型对象也是一个对象,这个对象原来的constructor属性指向Object构造函数,所以原来的constructor属性其实是Object.prototype原型对象的属性。第3行将constructor属性指向Person构造函数以后,当通过实例对象访问这个属性时,就直接返回Person构造函数,不再到Object.prototype原型对象中查找了。
在掌握了prototype、proto、constructor这些属性的使用以后,就可以在构造函数、原型对象、实例对象之间互相访问了,下面我们通过图1演示这三者的关系。
图1 构造函数、实例对象和原型对象互相访问