JavaScript 原型链:__proto__ 与 prototype 的关系
理解 JavaScript 中的原型链,核心在于搞清楚两个长得很像但作用完全不同的属性:prototype 和 __proto__。这不仅是面试的高频考点,更是理解 JS 对象继承机制的基石。
我们将通过具体的代码演示和内存逻辑,拆解这两者的关系。
1. 核心概念:谁是蓝图,谁是线索
在深入代码之前,先建立两个基本认知:
prototype(原型对象):它是函数特有的一块“预留仓库”。你在这个仓库里存放的东西,所有由该函数制造出来的对象都可以共用。__proto__(原型指针):它是对象身上自带的一根“线索”。这根线索指向制造它的那个函数的“预留仓库”。
2. 实操演示:建立连接
打开浏览器控制台,输入以下代码来验证两者如何关联。
第一步:定义构造函数
定义一个名为 Dog 的构造函数。此时,JS 引擎会自动给这个函数附带一个 prototype 属性,它是一个空对象。
function Dog(name) {
this.name = name;
}
第二步:添加共享方法
访问 Dog.prototype,并添加一个 bark 方法。相当于往“仓库”里放了一个工具。
Dog.prototype.bark = function() {
console.log("汪汪");
};
第三步:创建实例
使用 new 关键字创建一个实例对象 myDog。
const myDog = new Dog("旺财");
第四步:验证链接公式
输入以下等式进行验证。这是理解原型链最核心的数学关系:
$$ 实例.\_\_proto\_\_ === 构造函数.prototype $$
在控制台输入:
console.log(myDog.__proto__ === Dog.prototype);
观察输出结果,返回 true。这证明了 myDog 身上的 __proto__ 线索,精准地指向了 Dog 的 prototype 仓库。
3. 可视化结构:对象与原型的地图
为了更直观地理解这种指向关系,我们可以通过流程图来看清内存中的结构。
从图中可以看出:
Dog函数拥有prototype属性,指向原型对象。myDog实例拥有__proto__属性,也指向那个原型对象。- 两者最终指向了同一个内存地址。
4. 属性查找机制:顺着线索找工具
为什么我们在 myDog 身上没写 bark 方法,却能调用它?执行以下代码:
myDog.bark();
此时 JavaScript 引擎会执行以下查找步骤:
- 搜索
myDog自身:有没有bark?没有。 - 跟随
myDog.__proto__线索:找到了Dog.prototype。 - 搜索
Dog.prototype:有没有bark?有,执行它。 - 如果
Dog.prototype还没有,引擎会继续顺着Dog.prototype.__proto__往上找,直到找到Object.prototype或终点null。
这就是“原型链”名字的由来——由 __proto__ 串联起来的一条查找链条。
5. 终极链条:直到尽头
所有的原型链条最终都会汇合到一个终点。输入以下代码查看链条的顶端:
console.log(myDog.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
这说明:
Dog.prototype也是一个对象,它是Object的实例。Object.prototype是原型链的顶端,它的__proto__指向null,表示链条结束。
6. 对比总结表
为了防止混淆,请牢记下表的区别:
| 属性名 | 归属对象 | 作用 | 读写性 |
|---|---|---|---|
prototype |
函数 | 定义实例的共享属性和方法(仓库) | 可读写 |
__proto__ |
对象 | 指向构造函数的 prototype(线索) |
可读写(但不推荐) |
constructor |
prototype 对象 |
指回原来的构造函数 | 可读写 |
记住这个最实用的技巧:当你想知道一个对象能用什么方法时,查找它的 __proto__(或 Object.getPrototypeOf(obj));当你想给一类对象共享方法时,修改构造函数的 prototype。

暂无评论,快来抢沙发吧!