JavaScript 原型详解:从概念到实践

JavaScript 原型详解:从概念到实践

JavaScript 的面向对象编程与传统的类式面向对象不同,它基于原型(prototype) 实现。理解原型是掌握 JS 面向对象的核心,本文结合实例代码,详细解析原型的概念、工作原理及实践应用。

一、什么是原型?

在 JavaScript 中,每个函数都有一个特殊的属性prototype,它的值是一个对象(称为 "原型对象")。当通过new关键字调用函数(作为构造函数)创建实例时,实例会自动关联到构造函数的prototype对象,从而共享原型对象上的属性和方法。

用一句话概括:原型是构造函数的 "共享仓库",存储所有实例需要共享的属性和方法

二、构造函数与原型的关系

构造函数是用于创建实例的 "模板",而原型对象则是构造函数为实例准备的 "共享资源"。两者的关系可以通过代码直观体现:

示例 1:基础构造函数与原型

javascript 复制代码
// 定义构造函数(首字母大写,约定俗成)
function Person(name, age) {
  // 实例自身的属性(每个实例独立拥有)
  this.name = name;
  this.age = age;
}

// 原型对象:所有实例共享的属性
Person.prototype.species = '人类';

// 创建实例
const persona1 = new Person('张三', 18);
const persona2 = new Person('金总', 19);

// 实例可访问自身属性和原型属性
console.log(persona1.name); // 张三(自身属性)
console.log(persona1.species); // 人类(原型属性)
console.log(persona2.species); // 人类(原型属性)

代码解释

  • Person是构造函数,通过this为每个实例定义独立属性(nameage)。
  • Person.prototype是原型对象,定义了共享属性species,所有Person实例都能访问。
  • 即使创建多个实例,species属性只在原型中存储一份,实现了属性共享,节省内存。

三、实例与原型的关联:__proto__

每个实例对象都有一个私有属性__proto__(ES5 中可通过Object.getPrototypeOf()访问),它指向创建该实例的构造函数的prototype对象。这是实例能访问原型属性的核心原因。

示例 2:__proto__prototype的关系

javascript 复制代码
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// 重写原型对象(注意:用对象字面量重写时需手动指定constructor)
Person.prototype = {
  species: '人类',
  sayHi: function() {
    console.log(`你好,我是${this.name}`);
  }
};

const su = new Person('舒老板', 19);

// 实例的__proto__指向构造函数的prototype
console.log(su.__proto__ === Person.prototype); // true
console.log(Object.getPrototypeOf(su) === Person.prototype); // true(更推荐的写法)

代码解释

  • 实例su__proto__属性直接指向Person.prototype,因此能访问speciessayHi
  • Object.getPrototypeOf()是 ES5 规范中获取原型的方法,比__proto__更推荐使用。

图解

四、原型链:属性查找的规则

当访问实例的某个属性时,JS 会先在实例自身查找;如果找不到,会沿着__proto__指向的原型对象查找;如果原型对象中也没有,会继续查找原型对象的原型(即prototype.__proto__),直到找到null(原型链的终点)。这就是原型链

示例 3:原型链的查找机制

javascript 复制代码
function Person(name) {
  this.name = name;
}

// 原型对象1:Person.prototype
Person.prototype.species = '人类';

const su = new Person('舒老板');

// 1. 实例自身有name属性,直接返回
console.log(su.name); // 舒老板

// 2. 实例自身没有species,查找原型对象
console.log(su.species); // 人类

// 3. 实例和Person.prototype都没有toString,查找原型的原型(Object.prototype)
console.log(su.toString()); // [object Object](来自Object.prototype)

// 4. Object.prototype的原型是null,停止查找
console.log(Object.prototype.__proto__); // null

代码解释

  • su.toString()的查找路径:su自身 → Person.prototypeObject.prototype(找到toString方法)。
  • 所有对象的原型链最终都会指向Object.prototype,而Object.prototype.__proto__null,标志着原型链的结束。

图解

示例 4:实例属性覆盖原型属性

ini 复制代码
function Person(name) {
  this.name = name;
}

Person.prototype.species = '人类';

const su = new Person('舒老板');
su.species = 'LOL达人'; // 给实例添加同名属性

console.log(su.species); // LOL达人(优先访问实例自身属性)
console.log(su.__proto__.species); // 人类(原型属性未被修改)

代码解释

  • 当实例有与原型同名的属性时,会优先访问实例属性(不会修改原型属性)。
  • 若要修改原型属性,需直接操作构造函数.prototype.属性

五、原型继承

通过修改构造函数的prototype指向另一个构造函数的实例,可以实现原型继承,让子类实例共享父类原型的属性和方法。

示例 5:原型继承实现

javascript 复制代码
// 父构造函数
function Animal() {}
Animal.prototype.species = '动物';

// 子构造函数
function Person() {}

// 关键:让Person的原型指向Animal的实例,实现继承
Person.prototype = new Animal();

// 创建子类实例
const su = new Person();

console.log(su.species); // 动物(继承自Animal的原型)
console.log(su.__proto__); // Animal实例(Person.prototype)
console.log(su.__proto__.__proto__); // Animal.prototype(原型链的上一级)

代码解释

  • Person.prototype = new Animal()Person的原型成为Animal的实例,因此Person的实例su能通过原型链访问Animal.prototype上的species
  • 原型继承是 JS 实现继承的基础,本质是通过原型链共享父类资源。

六、总结

  1. 原型核心概念

    • 构造函数的prototype属性是原型对象,存储所有实例的共享属性 / 方法。
    • 实例的__proto__属性指向构造函数的prototype,是实例与原型的连接纽带。
  2. 原型链规则

    • 属性查找沿__proto__链条向上,直至null
    • 实例属性优先于原型属性。
  3. 实践价值

    • 通过原型实现属性 / 方法共享,减少内存消耗。
    • 通过原型链实现继承,构建复杂的对象关系。
相关推荐
BBBBBAAAAAi17 小时前
Claude Code安装记录
开发语言·前端·javascript
源码获取_wx:Fegn089518 小时前
基于 vue智慧养老院系统
开发语言·前端·javascript·vue.js·spring boot·后端·课程设计
Jing_Rainbow18 小时前
【 前端三剑客-37 /Lesson61(2025-12-09)】JavaScript 内存机制与执行原理详解🧠
前端·javascript·程序员
UIUV19 小时前
模块化CSS学习笔记:从作用域问题到实战解决方案
前端·javascript·react.js
Kakarotto19 小时前
使用ThreeJS绘制东方明珠塔模型
前端·javascript·vue.js
donecoding19 小时前
TypeScript `satisfies` 的核心价值:两个例子讲清楚
前端·javascript
Van_Moonlight19 小时前
RN for OpenHarmony 实战 TodoList 项目:顶部导航栏
javascript·开源·harmonyos
技术狂小子19 小时前
前端开发中那些看似微不足道却影响体验的细节
javascript
用户120391129472619 小时前
使用 Tailwind CSS 构建现代登录页面:从 Vite 配置到 React 交互细节
前端·javascript·react.js
花归去20 小时前
echarts 柱状图曲线图
开发语言·前端·javascript