原型-1: 理解 JavaScript 中的 原型

要理解 JavaScript 中的「原型」,首先要跳出其他面向对象语言(如 Java、C#)的「类」思维------JS 是通过 原型(Prototype) 实现继承和属性共享的,核心是「对象基于原型关联,而非类的实例化」。

一、先搞懂:为什么需要原型?

原型的本质是 "资源复用工具" 。它解决了一个核心问题:避免重复创建相同属性/方法,节省内存

举个例子:如果创建 100 个「人」对象,每个对象都需要 sayHello 方法。如果直接在每个对象里定义 sayHello,会生成 100 个完全相同的函数,造成内存浪费。

而原型的思路是: sayHello 放在一个"公共模板"(原型对象)里,所有「人」对象都"关联"这个模板,需要时直接从模板里拿方法 ------这样只需要 1 个 sayHello 函数,实现资源复用。

二、核心概念:3 个关键角色

理解原型,必须先分清 3 个紧密关联的概念:原型对象(Prototype)prototype 属性__proto__ 属性 (或 Object.getPrototypeOf() 方法)。

概念 作用 归属
原型对象(Prototype) 存储公共属性/方法的"模板对象",被其他对象共享 每个对象都有一个原型对象(除了 Object.prototype
prototype 属性 函数特有,指向该函数创建的「实例对象」的原型对象 函数(如构造函数、普通函数)
__proto__ 属性 对象特有,指向该对象的原型对象(非标准,但浏览器普遍支持) 所有对象(除了 null

三、直观理解:原型链的"关联关系"

所有对象的原型会形成一条「原型链」:对象 → 它的原型对象 → 原型的原型对象 → ... → 最终指向 Object.prototype(原型链的顶端),Object.prototype 的原型是 null(没有更上层的原型)。

我们通过一个具体例子,拆解原型链的关联逻辑:

1. 用构造函数创建对象(经典场景)

js 复制代码
// 1. 定义一个构造函数(首字母大写,约定用于创建对象)
function Person(name) {
  this.name = name; // 实例属性:每个 Person 实例都有独立的 name
}

// 2. 在构造函数的 prototype 上定义"公共方法"
Person.prototype.sayHello = function() {
  console.log(`Hello, I'm ${this.name}`);
};

// 3. 用 new 关键字创建 Person 实例
const zhangsan = new Person('张三');

2. 拆解原型关联(关键!)

上面代码中,zhangsanPersonPerson.prototype 三者的原型关系如下:

  • zhangsan (实例对象)的原型zhangsan.__proto__ === Person.prototype(或 Object.getPrototypeOf(zhangsan) === Person.prototype
    → 实例的原型,指向构造函数的 prototype 属性。
  • Person.prototype (原型对象)的原型Person.prototype.__proto__ === Object.prototype
    → 所有自定义原型对象,最终都关联到 Object.prototype(JS 内置的顶层原型)。
  • Object.prototype 的原型Object.prototype.__proto__ === null
    → 原型链的终点,没有更上层的原型。
  • Person (构造函数)的原型Person.__proto__ === Function.prototype
    → 函数也是对象,所有函数的原型都指向 Function.prototypeFunction.prototype.__proto__ 最终也指向 Object.prototype)。

3. 原型链的作用:属性查找规则

当你访问一个对象的属性(如 zhangsan.sayHello)时,JS 会按以下规则查找:

  1. 先在对象自身查找:如果 zhangsan 自己有 sayHello 方法,直接使用;
  2. 自身没有,就去「对象的原型对象」查找:即 Person.prototype,找到 sayHello,使用;
  3. 原型对象没有,就去「原型的原型」查找:即 Object.prototype
  4. 一直查到 null 还没找到,就返回 undefined(不会报错)。

比如:zhangsan.toString()toStringObject.prototype 的方法,zhangsan 自身没有,但能通过原型链找到)。

四、原型的核心特性

1. 共享性:原型上的属性/方法被所有实例共享

js 复制代码
const lisi = new Person('李四');
zhangsan.sayHello(); // Hello, I'm 张三
lisi.sayHello();     // Hello, I'm 李四
// 两个实例调用的是同一个 sayHello 方法(来自 Person.prototype)
console.log(zhangsan.sayHello === lisi.sayHello); // true

2. 动态性:原型的修改会影响所有已创建的实例

即使实例已经创建,修改原型对象的属性/方法,实例也能立即访问到新的内容:

js 复制代码
// 给 Person.prototype 新增一个方法
Person.prototype.eat = function() {
  console.log(`${this.name} is eating`);
};

// 已创建的 zhangsan 能直接调用新方法
zhangsan.eat(); // 张三 is eating

3. 实例可"覆盖"原型属性(不修改原型本身)

如果实例自身定义了和原型同名的属性/方法,会优先使用实例自身的(原型链查找时会先找到自身属性,停止向上查找):

js 复制代码
// 给 zhangsan 自身定义 sayHello 方法
zhangsan.sayHello = function() {
  console.log(`Hi, I'm ${this.name}`);
};

zhangsan.sayHello(); // Hi, I'm 张三(用自身的方法)
lisi.sayHello();     // Hello, I'm 李四(用原型的方法,不受影响)

五、常见误区澄清

  1. "原型是类"?错!
    JS 直到 ES6 才引入 class 语法,但 class 只是「原型的语法糖」------本质还是基于原型实现继承,并非像 Java 那样的"类"。
  2. prototype __proto__ 分不清?
    • 记住:prototype函数的属性 ,指向实例的原型;__proto__对象的属性,指向自己的原型。
    • 简单公式:实例.__proto__ === 构造函数.prototype
  1. "所有对象都有 __proto__ "?错!
    null 没有 __proto__Object.getPrototypeOf(null) 会返回 null),它是原型链的终点。

六、总结:原型的本质

原型的核心是「对象之间的关联关系 」------每个对象都通过 __proto__ 关联到一个原型对象,从而共享原型上的资源。这种关联形成的「原型链」,决定了 JS 中属性查找、继承的逻辑。

理解原型,就能理解 JS 面向对象的底层逻辑(比如 new 关键字的作用、继承的实现方式),也是后续学习 classextends 等语法的基础。

相关推荐
代码搬运媛3 小时前
Jest 测试框架详解与实现指南
前端
counterxing4 小时前
我把 Codex 里的 Skills 做成了一个 MCP,还支持分享
前端·agent·ai编程
wangqiaowq4 小时前
windows下nginx的安装
linux·服务器·前端
之歆4 小时前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
发现一只大呆瓜4 小时前
Vite凭什么这么快?3分钟带你彻底搞懂 Vite 热更新的幕后黑手
前端·面试·vite
Maimai108084 小时前
React如何用 @microsoft/fetch-event-source 落地 SSE:比原生 EventSource 更灵活的实时推送方案
前端·javascript·react.js·microsoft·前端框架·reactjs·webassembly
candyTong4 小时前
Claude Code 的 Edit 工具是怎么工作的
javascript·后端·架构
kyriewen6 小时前
产品经理把PRD写成“天书”,我用AI半小时重写了一遍,他当场愣住
前端·ai编程·cursor
humcomm7 小时前
元框架的工作原理详解
前端·前端框架
canonical_entropy7 小时前
Attractor Before Harness: AI 大规模开发的方法论
前端·aigc·ai编程