标签: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 → ✅ true
  • Person.prototype.constructor === Person → ✅ true
  • Person.__proto__ === Function.prototype → ✅ true(因为 Person 是函数)

💡 new Person() 创建的对象,其 __proto__ 自动指向 Person.prototype


三、原型链(Prototype Chain)是如何工作的?

当你访问一个对象的属性时,JavaScript 引擎会按以下顺序查找:

  1. 在对象自身查找;
  2. 如果没找到,沿着 __proto__ 向上查找;
  3. 重复此过程,直到 Object.prototype
  4. 若仍未找到,返回 undefined

示例:

const obj = {};
console.log(obj.toString()); // " [object Object] "

// 查找路径:
// obj → obj.__proto__ (=== Object.prototype) → 找到 toString

Object.prototype__proto__null,表示原型链终结:

Object.prototype.__proto__ === null; // true

四、ObjectFunction 的特殊关系(原型系统的基石)

JavaScript 的整个原型体系建立在两个内置对象之上:ObjectFunction

关键关系:

// 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 Objecttrue

因为数组的原型链:
[] → 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原型链的终点之一,提供 toStringhasOwnProperty 等方法
Function所有函数的构造器,包括 ObjectArray
最佳实践Object.create()Object.getPrototypeOf(),避免直接操作 __proto__

延伸阅读

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐