深入理解 JavaScript 原型系统:__proto__、prototype 与原型链之间的关系解析
标签:JavaScript、原型、原型链、
__proto__、prototype、前端基础、面试必备
在 JavaScript 中,“一切皆对象” 是一句耳熟能详的话。但真正理解这句话背后机制的人却不多。
为什么对象能调用 toString()?
为什么数组有 push() 方法?
为什么 obj instanceof Array 能判断类型?
答案都指向同一个核心机制:原型(Prototype)与原型链(Prototype Chain)。
本文将带你从零开始,彻底搞懂:
prototype是什么?__proto__又是什么?- 它们之间有何关系?
- 原型链如何工作?
- 以及常见的误区与最佳实践。
一、基本概念:prototype vs __proto__
每个 JavaScript 对象(除了
null)内部都有一个隐式链接,指向另一个对象 —— 这个被指向的对象就是它的原型。这个隐式链接在规范中叫
[[Prototype]],在代码中可通过以下方式访问:
- 非标准但广泛支持:
obj.__proto__- 标准方式:
Object.getPrototypeOf(obj)
这是最容易混淆的两个属性,但它们的归属和作用完全不同:
| 属性 | 所属 | 类型 | 作用 |
|---|---|---|---|
prototype | 函数(Function) | 对象 | 用于构造函数创建实例时,实例的 __proto__ 会指向它 |
__proto__ | 所有对象(包括函数) | 对象 | 指向该对象的原型(即内部 [[Prototype]]),用于属性查找 |
✅ 简单记:
- 只有函数才有
prototype- 所有对象都有
__proto__
二、一个例子看懂关系
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
const alice = new Person('Alice');
此时的关系如下:
alice.__proto__ === Person.prototype→ ✅ truePerson.prototype.constructor === Person→ ✅ truePerson.__proto__ === Function.prototype→ ✅ true(因为Person是函数)
💡
new Person()创建的对象,其__proto__自动指向Person.prototype
三、原型链(Prototype Chain)是如何工作的?
当你访问一个对象的属性时,JavaScript 引擎会按以下顺序查找:
- 在对象自身查找;
- 如果没找到,沿着
__proto__向上查找; - 重复此过程,直到
Object.prototype; - 若仍未找到,返回
undefined。
示例:
const obj = {};
console.log(obj.toString()); // " [object Object] "
// 查找路径:
// obj → obj.__proto__ (=== Object.prototype) → 找到 toString
而 Object.prototype 的 __proto__ 是 null,表示原型链终结:
Object.prototype.__proto__ === null; // true
四、Object 与 Function 的特殊关系(原型系统的基石)
JavaScript 的整个原型体系建立在两个内置对象之上:Object 和 Function。
关键关系:
// 1. Object 和 Function 都是函数
typeof Object; // "function"
typeof Function; // "function"
// 2. 它们都是 Function 的实例
Object instanceof Function; // true
Function instanceof Function; // true
// 3. __proto__ 指向
Object.__proto__ === Function.prototype; // true
Function.__proto__ === Function.prototype; // true
// 4. Function.prototype 是普通对象
Function.prototype.__proto__ === Object.prototype; // true
// 5. Object.prototype 是原型链顶端
Object.prototype.__proto__ === null; // true
原型关系图(文字版):
Function ──.__proto__──→ Function.prototype ──.__proto__──→ Object.prototype ──.__proto__──→ null
↑
│ (instanceof)
Object ──.__proto__───────────────────────────────────────┘
🔁 这看似“循环”,实则是 JS 引擎初始化时预设的闭环结构。
五、__proto__ 是标准吗?推荐用法是什么?
虽然 obj.__proto__ 在所有现代浏览器中都支持,但它不是 ECMAScript 标准的一部分(仅在附录 B 作为遗留特性保留)。
✅ 推荐使用标准 API:
| 操作 | 推荐写法 | 说明 |
|---|---|---|
| 获取原型 | Object.getPrototypeOf(obj) | 标准、安全 |
| 设置原型 | Object.setPrototypeOf(obj, proto) | 谨慎使用(性能差) |
| 创建指定原型的对象 | Object.create(proto) | 高效、常用 |
❌ 不推荐:
obj.__proto__ = SomePrototype; // 非标准,破坏引擎优化
六、常见误区与面试题
1. typeof null 为什么是 "object"?
这是 JavaScript 初期的一个历史性 bug(因底层类型标签用 3 位表示,
null的机器码为 000,被误判为对象),但为了兼容性一直保留至今。
2. 为什么 [] instanceof Object 是 true?
因为数组的原型链:
[] → Array.prototype → Object.prototype
所以[]也是Object的实例。
3. 修改 prototype 会影响已创建的实例吗?
会! 因为实例通过
__proto__引用原型对象,不是拷贝。
function Foo() {}
const a = new Foo();
Foo.prototype.newMethod = () => 'hi';
console.log(a.newMethod()); // "hi" ✅
七、总结:一张表理清核心概念
| 概念 | 说明 |
|---|---|
prototype | 函数独有的属性,用于 new 创建的对象继承 |
__proto__ | 所有对象都有,指向其原型(即 [[Prototype]]) |
| 原型链 | 属性查找机制:沿 __proto__ 向上直到 null |
Object.prototype | 原型链的终点之一,提供 toString、hasOwnProperty 等方法 |
Function | 所有函数的构造器,包括 Object、Array 等 |
| 最佳实践 | 用 Object.create()、Object.getPrototypeOf(),避免直接操作 __proto__ |
延伸阅读
- MDN: 继承与原型链
- ECMAScript 规范:
[[Prototype]]、[[Call]]内部方法
更多推荐



所有评论(0)