原型到底是什么?一篇带你真正读懂 JavaScript 原型与原型链

67 阅读4分钟

在 JavaScript 的世界里,“原型(prototype)” 是你绕不过去的核心机制。
无论你使用 ES5 的构造函数,还是 ES6 的 class,底层的面向对象系统仍然是 原型式(Prototype-based) 的。

很多同学说:

“prototype 太抽象了,好难懂!”
proto 又是啥?和 prototype 有啥关系?”
“原型链到底是怎么查找的?”

别急,这篇文章会从 底层机制 → 实例构造 → 原型对象 → 原型链查找 → 继承机制 → constructor 修复 等角度,配合大量图解,帮你彻底搞懂 JS 原型体系。


🚗 1. 构造函数与实例:一切的起点

在 ES5 中,没有 class,所谓“类”是用构造函数模拟出来的

function Car(color) {
    this.color = color;
}

当你这样调用时:

const car1 = new Car("霞光紫");

发生了什么?

✔ new 执行四步:

  1. 创建一个空对象 {}
  2. 让这个对象的 __proto__ 指向构造函数的 prototype
  3. 以新对象为 this,执行构造函数
  4. 返回新对象

🧬 2. prototype:构造函数的“实例原型对象”

构造函数 Car 有一个重要属性:

Car.prototype

它是“原型对象”,存放所有实例 共享的属性和方法

Car.prototype = {
    drive() {
        console.log('drive, 下赛道');
    },
    name: 'su7',
    height : 1.4,
    weight : 1.5
};

如果实例 car1 没有某个属性,JS 就会自动去 Car.prototype 上找。

lQLPJxXUsW--hqPM0M0CQ7AG2mNpaMSkTwkAVqR24rcB_579_208.png


🧩 3. proto:实例对象的隐藏原型指针

__proto__ 是每个对象都有的一个 隐藏属性,指向它的原型:

car1.__proto__ === Car.prototype  // true

它不是标准属性(但浏览器普遍支持),推荐用:

Object.getPrototypeOf(car1)

🔭 4. 构造函数 → 实例原型 → 实例

lQLPKHe4Xo7EluPNAiTNA5mw43bK8jeB_YAJAF1fqCkwAA_921_548.png

  • 构造函数 Person 有属性 prototype
  • 实例 person 通过 __proto__ 指向 Person.prototype
  • 于是实例就可以“访问”原型的属性与方法

🧠 5. prototype 与 proto 的关系(必须死记)

属性归属指向
prototype构造函数实例原型对象
proto实例对象指向构造函数的 prototype

一句话总结:

prototype 是构造函数的属性
proto 是实例对象的属性
最终它们两个指向同一份原型对象


🌲 6. 深挖:原型链的真正含义

访问对象属性时,JS 采用的是 原型链查找机制

car1.drive()

查找顺序是:

  1. 先看 car1 自己有没有 drive
  2. 没有 → 去 car1.proto(Car.prototype)上找
  3. 再没有 → 去 Car.prototype.proto(Object.prototype)
  4. 再没有 → 到达 null,查找结束

这就是 原型链(prototype chain)


🧱 7. 构造函数 ↔ prototype 的双向关系

lQLPJwvG0tJ1vuPNASLNAkSwAikK2ZgDD2YJAF6EAku5AA_580_290.png

  • 构造函数有 prototype → 指向原型对象
  • 原型对象有 constructor → 指回构造函数

这是一种天然的“双向绑定”。


🚀 8. 原型链最终都会指向 Object.prototype

来看这张图:

lQLPKILwjfLIS2PNAd3NAk6wNuy8q5kFWPEJAGtK0Z0KAA_590_477.png

无论你是 Person、Car 还是其他构造函数:

Person.prototype.__proto__ === Object.prototype

而:

Object.prototype.__proto__ === null

到 null,链条结束。


🧬 9. ES5 原型继承:借助原型链实现继承

例如:

function Animal() {}
Animal.prototype.species = '动物';

function Person() {}
Person.prototype = new Animal();

const su = new Person();

此时 su 的原型链:

su → Person.prototypeAnimal.prototypeObject.prototypenull

你看到 su 可以访问到:

su.species

因为通过原型链查找找到了 Animal.prototype.species。

并且你也看到了:

console.log(su.toString)

说明 su 也继承了 Object.prototype 的方法。


🔁 10. 重写 prototype 后必须手动修复 constructor

默认情况下,构造函数有一个 prototype,其 constructor 指向自身:

Person.prototype.constructor === Person

但当你这样写时:

Person.prototype = {
    speci: '人类'
};

新的对象字面量没有 constructor 属性,于是它会从 Object.prototype 继承 constructor:

Person.prototype.constructor === Object  // ❌ 错误

这会导致依赖 constructor 的代码出问题。

所以必须手动修复:

Person.prototype = {
    speci: '人类',
    constructor: Person
};

这是非常重要的“行业最佳实践”。


🎯 11. 原型式面向对象 vs 经典面向对象

传统 OOP(Java、C++)基于 class:

  • 类是模板
  • 实例是复制品
  • 数据与结构是静态继承

而 JS:

  • 没有“类继承链”,只有“原型链”
  • 每个对象都可以作为另一个对象的原型
  • 原型可以动态修改,所有实例立即生效

这就是 JS 的哲学:

万物皆对象,对象继承于对象,而不是类。


🏛 12. ES6 class 其实是语法糖,本质还是原型

例如:

class Person {
    constructor(name) {
        this.name = name;
    }
    sayHi() {
        console.log(`Hi, I'm ${this.name}`);
    }
}

等价于 ES5 写法:

function Person(name) {
    this.name = name;
}
Person.prototype.sayHi = function() {
    console.log(`Hi, I'm ${this.name}`);
};

本质不变:

  • 依然使用 prototype 保存方法
  • 依然通过 proto 链接原型链
  • 依然基于 Object.prototype

🧩 13. 全流程理解图(核心总览)

下面这张图是最完整、最重要的一张(建议收藏):

lQLPJyCHLn1iTmPNAg3NAk6wOERTvHM11HQJAGwnY_a5AA_590_525.png

它展示了:

  • 构造函数 prototype
  • 实例 proto
  • Object.prototype
  • null 作为终止点
  • constructor 的指向关系

看到这张图,你就理解了整个 JS 原型体系。


🌟 14. 总结(送你一套最精华记忆口诀)

✔ 第一套:“prototype 属于函数,proto 属于对象”

prototype 是构造函数属性  
__proto__ 是实例属性  
最终指向同一个原型对象

✔ 第二套:“方法在原型上,属性在实例上”

实例属性:每个实例独立  
原型属性:所有实例共享  

✔ 第三套:“查不到就沿 proto 往上找”

对象 → 构造函数 prototype → Object.prototypenull

✔ 第四套:“重写原型别忘 constructor”

Person.prototype = { ... }  → 必须补 constructor

✔ 第五套:“class 只是语法糖,本质还是原型”

class 的底层依然使用 prototype / __proto__

📌 结语

如果你能看懂所有图、理解每行代码,并且能够默画一张类似的原型链关系图,那么恭喜你:

你真的已经彻底掌握 JavaScript 原型与原型链系统了!