原型-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 等语法的基础。

相关推荐
HIT_Weston几秒前
57、【Ubuntu】【Gitlab】拉出内网 Web 服务:Gitlab 配置审视(一)
前端·ubuntu·gitlab
用户6600676685391 分钟前
模板字符串 + map:用现代 JavaScript 高效构建动态 HTML
前端·javascript
AY呀2 分钟前
《玩转Vue3响应式:手把手实现TodoList,掌握核心指令》
前端·javascript·vue.js
哆啦A梦158812 分钟前
商城后台管理系统 07 商品列表-分页实现
前端·javascript·vue.js
爱因斯坦乐13 分钟前
【若依】前后端分离添加导入
java·前端·javascript
Cache技术分享13 分钟前
267. Java 集合 - Java 开发必看:ArrayList 与 LinkedList 的全方位对比及选择建议
前端·后端
答案answer17 分钟前
Vue3项目集成monaco-editor实现浏览器IDE代码编辑功能
前端·vue.js
爱上妖精的尾巴22 分钟前
6-1WPS JS宏 new Set集合的创建
前端·后端·restful·wps·js宏·jsa
绝世唐门三哥23 分钟前
Vue 自定义指令完全指南(含 Vue2/Vue3 对比 + 完整 Demo)
前端·javascript·vue.js
uhakadotcom24 分钟前
Tomli 全面教程:常用 API 串联与实战指南
前端·面试·github