跳到主要内容

prototype proto 和 constructor

new 关键字

当我们使用new关键字执行构造函数时,函数内部的this会指向新创建的实例对象。

function Person() {
this.name = 'Tom';
}
var person = new Person();
console.log(person);

执行上述代码后,实例对象person上就挂载了一个属性name: 'Tom'

原型对象

在 JavaScript 中,每个函数都有一个prototype属性,指向该函数的原型对象。原型对象是所有通过该构造函数创建的实例对象的公共祖先,实例对象可以访问原型对象上的属性和方法。

function Person() {
this.name = 'Tom';
}
Person.prototype.age = 20;

var person = new Person();
console.log(person.age); // 20

实例对象person可以访问到原型对象上的age属性。

访问原型对象的两种方式

我们可以通过以下两种方式来访问一个对象的原型:

  1. 通过实例对象的__proto__属性
  2. 通过构造函数的prototype属性
function Person() {
this.name = 'Tom';
}
Person.prototype.age = 20;

var person = new Person();
console.log(person.__proto__ === Person.prototype); // true

实例对象的__proto__属性和构造函数的prototype属性指向同一个原型对象。

实例对象与 Object 原型的关系

在 JavaScript 中,几乎所有对象都是Object的实例,因此它们的原型链最终都会指向Object.prototype。但实例对象的原型和Object.prototype并不是同一个对象。

function Person() {
this.name = 'Tom';
}
var person = new Person();
console.log(person.__proto__ === Object.prototype); // false
console.log(person.__proto__.__proto__ === Object.prototype); // true

实例对象person的原型是Person.prototype,而Person.prototype的原型才是Object.prototype

属性的颜色区分

在浏览器控制台中查看对象时,不同颜色的属性有不同的含义:

  1. 淡紫色表示系统内置的属性
  2. 深紫色表示用户自定义的属性
function Person() {
this.name = 'Tom';
}
var person = new Person();
console.log(person);

访问原型对象的最佳实践

虽然我们可以通过实例对象的__proto__属性来访问原型对象,但这种做法并不推荐。更好的方式是使用Object.getPrototypeOf()方法。

function Person() {
this.name = 'Tom';
}
var person = new Person();

console.log(person.__proto__ === Person.prototype); // true
console.log(Object.getPrototypeOf(person) === Person.prototype); // true

使用Object.getPrototypeOf()可以避免直接访问__proto__可能带来的问题。

constructor 属性

每个原型对象都有一个constructor属性,指向与之关联的构造函数。

function Person() {
this.name = 'Tom';
}
var person = new Person();
console.log(person);

可以看到,Person.prototype.constructor指向了Person构造函数本身。

重写原型对象与 constructor

如果我们直接重写一个函数的原型对象,那么新原型对象的constructor属性会指向Object构造函数,而不是原来的构造函数。

function Person() {
this.name = 'Tom';
}
Person.prototype = {
age: 20,
sayHi: function () {
console.log('Hi');
},
};
var person = new Person();
console.log(person);

重写Person.prototype后,新原型对象的constructor指向了Object,而不是Person

为了保持constructor属性的正确性,我们在重写原型对象时,需要手动恢复constructor属性:

function Person() {
this.name = 'Tom';
}
Person.prototype = {
age: 20,
sayHi: function () {
console.log('Hi');
},
constructor: Person,
};
var person = new Person();
console.log(person);

这样修改后,Person.prototype.constructor又重新指向了Person构造函数。