【js篇】深入理解 JavaScript 原型与原型链

在 JavaScript 的世界中,原型(Prototype)和原型链(Prototype Chain) 是每个前端开发者都必须掌握的核心概念。它们不仅是 JavaScript 实现面向对象编程的基础,更是理解函数、对象、继承机制的关键所在。

今天,我们就来系统地梳理一下原型与原型链的底层原理,帮助你彻底搞懂这个"看似复杂、实则清晰"的机制。


一、什么是原型?构造函数与 prototype 的关系

在 JavaScript 中,我们通常使用构造函数来创建对象实例。例如:

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

const person1 = new Person("Alice");
const person2 = new Person("Bob");

每个构造函数都有一个内置属性:prototype。这个属性指向一个对象,该对象包含了所有实例可以共享的属性和方法。

javascript 复制代码
Person.prototype.sayHello = function() {
  console.log(`Hello, I'm ${this.name}`);
};

person1.sayHello(); // 输出: Hello, I'm Alice
person2.sayHello(); // 输出: Hello, I'm Bob

关键点:

  • Person.prototype 是一个对象。
  • 所有通过 new Person() 创建的实例,都可以访问 prototype 上定义的方法和属性。
  • 这种方式避免了在每个实例中重复定义相同的方法,节省内存,提升性能。

二、对象的原型:__proto__Object.getPrototypeOf()

当我们使用构造函数创建一个对象时,这个对象内部会自动包含一个指向其构造函数 prototype 的指针 ,这个指针就是对象的原型

在 ES5 之前,虽然这个指针是内部机制,但现代浏览器普遍实现了 __proto__ 属性来访问它:

javascript 复制代码
console.log(person1.__proto__ === Person.prototype); // true

⚠️ 但请注意:

  • __proto__ 不是标准推荐使用的属性,尽管大多数环境支持它。
  • ES5 引入了标准方法 Object.getPrototypeOf() 来安全获取对象的原型:
javascript 复制代码
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true ✅ 推荐使用

📌 小贴士:始终优先使用 Object.getPrototypeOf(obj) 而不是 obj.__proto__,以保证代码的规范性和可移植性。


三、原型链:属性查找的"向上追溯"机制

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

  1. 先在对象自身查找;
  2. 如果找不到,就去它的原型对象中查找;
  3. 如果原型中也没有,就继续查找原型的原型
  4. 一直向上追溯,直到找到属性或原型链结束。

这个逐层向上查找的过程,就是 原型链(Prototype Chain)

🔗 原型链的终点:Object.prototype

所有对象的原型链最终都会指向 Object.prototype,而 Object.prototype.__proto__null,表示链的终点。

javascript 复制代码
console.log(Object.getPrototypeOf(Object.prototype)); // null

这也是为什么我们创建的任意对象都能调用 toString()hasOwnProperty() 等方法的原因:

javascript 复制代码
person1.toString(); // 继承自 Object.prototype

🧩 原型链示意图

javascript 复制代码
person1
  ↓ __proto__
Person.prototype
  ↓ __proto__
Object.prototype
  ↓ __proto__
null

四、原型的引用特性:共享与动态继承

JavaScript 中的对象是通过引用传递的。这意味着:

  • 所有实例共享同一个原型对象;
  • 原型对象不会被每个实例复制一份;
  • 当你修改原型时,所有实例都会"动态"继承这些变化。

✅ 动态修改原型的示例

javascript 复制代码
// 先创建实例
const person = new Person("Charlie");

// 后添加方法到原型
Person.prototype.greet = function() {
  console.log("Hi there!");
};

// 实例仍然可以访问新方法
person.greet(); // 输出: Hi there!

💡 这种"动态性"是 JavaScript 灵活性的体现,但也需谨慎使用,避免在生产环境中随意修改原生对象的原型。


五、常见误区与最佳实践

❌ 误区1:混淆 prototype__proto__

  • prototype构造函数的属性;
  • __proto__实例对象的原型指针;
  • 两者指向的是同一个对象(对于实例而言)。

❌ 误区2:认为原型是"副本"

原型是引用共享,不是复制。修改原型会影响所有实例。

✅ 最佳实践建议

  1. 使用 Object.create(null) 创建无原型链的对象(用于纯字典场景);
  2. 优先使用 Object.getPrototypeOf()Object.setPrototypeOf()
  3. 避免修改原生对象(如 Array.prototype)的原型;
  4. 理解 class 语法糖背后的原型机制(ES6 的 class 本质仍是基于原型);

六、总结:原型与原型链的核心要点

概念 说明
prototype 构造函数的属性,存储共享方法和属性
__proto__ 实例的内部原型指针(不推荐直接使用)
Object.getPrototypeOf() 标准方法,获取对象的原型 ✅
原型链 属性查找时逐层向上追溯的机制
终点 Object.prototype,其 __proto__null
特性 引用共享、动态继承、内存高效

结语

原型与原型链是 JavaScript 的"灵魂"之一。理解它,不仅能帮助你写出更高效的代码,还能在面试、调试、框架源码阅读中游刃有余。

🔥 记住一句话:

"每个对象都有一个原型,查找属性时会沿着原型链一路向上,直到 null。"

相关推荐
文心快码BaiduComate2 小时前
文心快码入选2025服贸会“数智影响力”先锋案例
前端·后端·程序员
云枫晖2 小时前
手写Promise-构造函数
前端·javascript
文心快码BaiduComate2 小时前
用Comate Zulu开发一款微信小程序
前端·后端·微信小程序
王王碎冰冰2 小时前
基于 Vue3@3.5+跟Ant Design of Vue 的二次封装的 Form跟搜索Table
前端·vue.js
naice3 小时前
我对github的图片很不爽了,于是用AI写了一个图片预览插件
前端·javascript·git
天蓝色的鱼鱼3 小时前
Element UI 2.X 主题定制完整指南:解决官方工具失效的实战方案
前端·vue.js
RoyLin3 小时前
TypeScript设计模式:门面模式
前端·后端·typescript
小奋斗3 小时前
千量数据级别的数据统计分析渲染
前端·javascript
小文刀6963 小时前
CSS-响应式布局
前端