原型(Prototype)与原型链(Prototype Chain)—— JavaScript 的核心机制
JavaScript 是一门基于原型的语言,原型和原型链是它实现继承和属性查找的核心机制。理解它们,才能真正掌握 JavaScript 的面向对象编程。
1. 原型(Prototype)是什么?
(1) 每个 JavaScript 对象都有一个原型(__proto__ 或 [[Prototype]])
- 对象通过 原型链 继承属性和方法。
- 原型本身也是一个对象,因此它也有自己的原型,形成链条。
(2) 如何查看对象的原型?
-
obj.__proto__(非标准,但广泛支持) -
Object.getPrototypeOf(obj)(标准方法)
const obj = {};
console.log(obj.__proto__ === Object.prototype); // true
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
(3) 函数的原型(prototype 属性)
- 函数(如构造函数)有一个特殊的
prototype属性,用于实现基于原型的继承。 -
new操作符 会将该函数的prototype赋值给新对象的__proto__。
function Person() {}
console.log(Person.prototype); // { constructor: Person }
const p = new Person();
console.log(p.__proto__ === Person.prototype); // true
2. 原型链(Prototype Chain)是什么?
(1) 原型链是 JavaScript 的继承机制
- 当访问一个对象的属性时,如果它自身没有该属性,JavaScript 会沿着 原型链 向上查找。
- 查找顺序:
对象自身 → 对象的proto→proto.proto→ ... →Object.prototype→null 。
(2) 原型链示例
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(`${this.name} is eating.`);
};
function Dog(name, breed) {
Animal.call(this, name); // 调用父类构造函数
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype); // 继承 Animal 的原型
Dog.prototype.constructor = Dog; // 修复 constructor 指向
Dog.prototype.bark = function() {
console.log(`${this.name} is barking.`);
};
const dog = new Dog("Buddy", "Golden Retriever");
dog.eat(); // 继承自 Animal
dog.bark(); // Dog 自身的方法
原型链关系:
dog (Dog 实例)
├── __proto__ → Dog.prototype
├── __proto__ → Animal.prototype
├── __proto__ → Object.prototype
├── __proto__ → null
(3) Object.prototype 是原型链的顶端
- 所有对象最终都继承自
Object.prototype。 Object.prototype.__proto__是null,表示原型链的终点。
console.log(Object.prototype.__proto__); // null
3. 原型 vs 原型链
| 概念 | 说明 |
|---|---|
| 原型(Prototype) | 每个对象都有一个原型(__proto__),函数还有一个 prototype 属性。 |
| 原型链(Prototype Chain) | 对象通过 __proto__ 形成的链条,用于属性查找。 |
4. 原型链的应用
(1) 继承(ES5 的继承方式)
function Parent() {
this.parentProp = "Parent";
}
Parent.prototype.parentMethod = function() {
console.log("Parent method");
};
function Child() {
Parent.call(this); // 调用父类构造函数
this.childProp = "Child";
}
Child.prototype = Object.create(Parent.prototype); // 继承父类原型
Child.prototype.constructor = Child; // 修复 constructor
const child = new Child();
child.parentMethod(); // 继承自 Parent
(2) 方法共享
- 所有实例共享原型上的方法,节省内存。
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, ${this.name}!`);
};
const p1 = new Person("Alice");
const p2 = new Person("Bob");
p1.sayHello === p2.sayHello; // true(共享同一个方法)
(3) 属性查找机制
- 当访问
obj.prop时:- 先在
obj自身查找。 - 如果没有,沿着
__proto__向上查找。 - 直到
Object.prototype或null。
- 先在
const obj = {};
console.log(obj.toString()); // 来自 Object.prototype.toString
5. ES6 的 class 和原型链
ES6 的 class 本质上是语法糖,底层仍然是基于原型的继承。
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log(`${this.name} is eating.`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
bark() {
console.log(`${this.name} is barking.`);
}
}
const dog = new Dog("Buddy", "Golden Retriever");
dog.eat(); // 继承自 Animal
dog.bark(); // Dog 自身的方法
底层原型链:
dog (Dog 实例)
├── __proto__ → Dog.prototype
├── __proto__ → Animal.prototype
├── __proto__ → Object.prototype
├── __proto__ → null
6. 常见问题
(1) 为什么 obj.toString() 能调用,但 obj 没有 toString 方法?
- 因为
toString定义在Object.prototype上,obj通过原型链找到它。
(2) 如何避免原型污染?
- 不要直接修改
Object.prototype,否则会影响所有对象。 - 使用
Object.create(null)创建无原型的对象(用于纯字典)。
const pureDict = Object.create(null);
pureDict.key = "value";
console.log(pureDict.toString); // undefined(不会继承 Object.prototype 的方法)
(3) __proto__ 和 prototype 的区别?
| 属性 | 说明 |
|---|---|
obj.__proto__ | 对象的原型(指向其构造函数的 prototype)。 |
func.prototype | 函数的原型(用于 new 创建实例时的原型)。 |
7. 总结
- 原型:每个对象都有一个原型(
__proto__),函数还有一个prototype属性。 - 原型链:对象通过
__proto__形成的链条,用于属性查找。 - 继承:ES5 通过
Parent.prototype和Object.create实现,ES6 通过class和extends实现。 - 方法共享:原型上的方法被所有实例共享,节省内存。
- 属性查找:先在自身查找,再沿原型链向上查找。
一句话:
JavaScript 的继承和属性查找机制完全依赖原型和原型链,理解它们是掌握 JS 面向对象编程的关键!
4810

被折叠的 条评论
为什么被折叠?



