JavaScript 原型链:理解对象继承的核心机制
在 JavaScript 中,原型链(Prototype Chain) 是实现对象继承和属性查找的核心机制。与传统面向对象语言(如 Java、C++)基于"类"的继承不同,JavaScript 采用的是 基于原型的继承模型 。本文将结合 Promise 实例和普通构造函数示例,深入浅出地解析原型链的工作原理。
一、什么是原型链?
每个 JavaScript 对象(除 null 外)内部都有一个隐藏属性 [[Prototype]](可通过 __proto__ 访问),它指向另一个对象------这个对象就是该对象的"原型"。当试图访问一个对象的属性时,如果该对象自身没有这个属性,JavaScript 引擎会沿着原型链向上查找,直到找到该属性或到达原型链的末端(即 null)。
关键点:JavaScript 的继承不是靠"血缘",而是靠"链条"------原型链。
二、构造函数、原型对象与实例的关系
以自定义构造函数为例:
ini
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.species = '人';
let zeng = new Person('jw', 18);
Person是一个构造函数。Person.prototype是一个普通对象,所有通过new Person()创建的实例都会以它为原型。zeng.__proto__ === Person.prototype→ 成立Person.prototype.constructor === Person→ 成立
这种结构形成了经典的"三角关系":
- 实例(
zeng)通过__proto__指向原型对象(Person.prototype) - 原型对象通过
constructor指回构造函数(Person)
🚂 比喻:可以把
constructor看作火车头,prototype是车身,而每个实例是挂在车身后的车厢。它们通过"挂钩"(__proto__)连接在一起。
三、动态修改原型:打破常规
JavaScript 的原型是可变的 。我们可以随时修改对象的 __proto__:
ini
const kong = {
name: 'kong',
hobbies: ['篮球', '足球'],
};
zeng.__proto__ = kong;
console.log(zeng.hobbies); // ['篮球', '足球']
console.log(zeng.species); // undefined
此时:
zeng不再从Person.prototype继承属性;- 而是从
kong对象继承; - 因此
species找不到了,但hobbies可以访问。
⚠️ 注意:虽然技术上可行,但不推荐随意修改
__proto__,因为它会破坏性能优化,并可能导致代码难以维护。
四、内置对象也遵循原型链:以 Promise 为例
ES6 引入的 Promise 同样遵循原型链规则:
javascript
const p = new Promise((resolve, reject) => {
setTimeout(() => reject('失败1'), 3000);
});
p是一个Promise实例;p.__proto__ === Promise.prototype→ truePromise.prototype上定义了.then(),.catch(),.finally()等方法;- 所以
p.then(...)实际上调用的是Promise.prototype.then
执行流程:
new Promise(...)立即执行 executor 函数(同步)→ 输出'111'- 主线程继续执行 → 输出
'222'和p的初始状态(pending) - 3 秒后,
reject('失败1')触发状态变为rejected - 微任务队列中安排
.catch()回调 → 输出'失败1' .finally()总是执行 → 输出'finally'
这再次印证:所有对象的行为都依赖于其原型链上的方法。
五、原型链的本质:属性查找机制
当你写 obj.method() 时,JavaScript 引擎会:
- 在
obj自身查找method - 若无,则查找
obj.__proto__ - 若仍无,继续查找
obj.__proto__.__proto__ - ......直到
Object.prototype(最顶层) - 若最终找不到,返回
undefined
例如:
scss
zeng.toString(); // 虽然 zeng 自身没有 toString,但 Object.prototype 有
因为:
ini
zeng
→ __proto__ = kong
→ __proto__ = Object.prototype
→ has toString()
六、总结
| 概念 | 说明 |
|---|---|
__proto__ |
实例指向其原型的链接(非标准但广泛支持) |
prototype |
构造函数的属性,用于被实例的 __proto__ 引用 |
constructor |
原型对象上的属性,指回构造函数 |
| 原型链 | 属性/方法查找的路径,从实例 → 原型 → 原型的原型 → ... → null |
JavaScript 的面向对象不是靠"类继承",而是靠"对象委托"------你没有的,我帮你问我的原型要。这种灵活而强大的机制,正是 JavaScript 动态特性的基石。
✅ 牢记:一切皆对象,万物皆可链。理解原型链,就掌握了 JavaScript 面向对象的灵魂。