【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。"

相关推荐
初学小白...8 小时前
HTML知识点
前端·javascript·html
鹏多多8 小时前
flutter睡眠与冥想数据可视化神器:sleep_stage_chart插件全解析
android·前端·flutter
艾小码8 小时前
Vue3 脚本革命:<script setup> 让你的代码简洁到飞起!
前端·javascript·vue.js
U***e638 小时前
JavaScript在Node.js中的Webpack
javascript·webpack·node.js
IT_陈寒9 小时前
Python 3.12新特性解析:10个让你代码效率提升30%的实用技巧
前端·人工智能·后端
故厶9 小时前
webpack实战
前端·javascript·webpack
_果果然9 小时前
你真的懂递归吗?没那么复杂,但也没那么简单
前端·javascript
菜泡泡@10 小时前
仓库地图vue-grid-layout
前端·javascript·vue.js
u***u68512 小时前
React环境
前端·react.js·前端框架
X***E46312 小时前
前端数据分析应用
前端·数据挖掘·数据分析