原型与原型链
理解 __proto__、prototype 和 constructor 的关系
问题
解释 JavaScript 中的原型与原型链机制,说明 __proto__、prototype 和 constructor 三者的关系。
解答
三个属性的定义
function Person(name) {
this.name = name;
}
const p = new Person('张三');
// prototype: 函数的属性,指向原型对象
console.log(Person.prototype); // { constructor: Person }
// __proto__: 实例的属性,指向构造函数的 prototype
console.log(p.__proto__ === Person.prototype); // true
// constructor: 原型对象的属性,指向构造函数
console.log(Person.prototype.constructor === Person); // true
三者的关系图
┌─────────────────────────────────────┐
│ │
▼ │
┌─────────────┐ prototype ┌──────────────────┐ constructor
│ Person │ ──────────► │ Person.prototype │ ─────────┘
│ (构造函数) │ │ (原型对象) │
└─────────────┘ └──────────────────┘
│ ▲
│ new │ __proto__
▼ │
┌─────────────┐ │
│ p │ ─────────────────────┘
│ (实例) │
└─────────────┘
原型链的形成
function Animal(type) {
this.type = type;
}
Animal.prototype.eat = function() {
console.log('eating');
};
function Dog(name) {
Animal.call(this, 'dog');
this.name = name;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log('woof!');
};
const dog = new Dog('旺财');
// 原型链查找过程
console.log(dog.name); // '旺财' - 自身属性
console.log(dog.bark); // function - Dog.prototype 上
console.log(dog.eat); // function - Animal.prototype 上
console.log(dog.toString); // function - Object.prototype 上
// 原型链
console.log(dog.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true - 链的终点
验证原型链
// instanceof 沿原型链查找
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true
// isPrototypeOf 检查是否在原型链上
console.log(Dog.prototype.isPrototypeOf(dog)); // true
console.log(Animal.prototype.isPrototypeOf(dog)); // true
// getPrototypeOf 获取原型(推荐替代 __proto__)
console.log(Object.getPrototypeOf(dog) === Dog.prototype); // true
属性查找与遮蔽
function Foo() {}
Foo.prototype.x = 1;
const foo = new Foo();
console.log(foo.x); // 1 - 来自原型
foo.x = 2; // 在实例上创建属性,遮蔽原型属性
console.log(foo.x); // 2 - 来自实例
console.log(foo.__proto__.x); // 1 - 原型上的值不变
// hasOwnProperty 区分自身属性和继承属性
console.log(foo.hasOwnProperty('x')); // true
delete foo.x;
console.log(foo.hasOwnProperty('x')); // false
console.log(foo.x); // 1 - 又能访问原型上的了
关键点
- prototype 是函数独有的属性,指向原型对象,用于实现继承
- __proto__ 是对象的属性,指向其构造函数的 prototype,形成原型链
- constructor 是原型对象的属性,指回构造函数本身
- 属性查找沿原型链向上,直到
Object.prototype.__proto__(null)为止 - 推荐用
Object.getPrototypeOf()替代__proto__,后者是非标准属性
目录