深入理解 JavaScript 原型链:构造函数、实例与原型三角关系

在 JavaScript 中,原型继承 是核心特性之一,它决定了对象如何共享属性和方法。然而,许多开发者对 prototype__proto__constructor 的关系感到困惑。本文将通过代码示例和图解,彻底理清构造函数、实例和原型之间的"三角关系"。


1. 构造函数与原型对象

1.1 什么是构造函数?

在 JavaScript 中,构造函数 是一个普通函数,但通过 new 关键字调用时,它会创建一个新对象并初始化其属性。例如:

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

1.2 构造函数的 prototype 属性

每个函数(包括构造函数)都有一个 prototype 属性,它指向一个原型对象 。这个对象默认包含一个 constructor 属性,指向构造函数本身:

javascript 复制代码
console.log(User.prototype); 
// 输出:
// {
//   constructor: ƒ User(name),
//   __proto__: Object.prototype
// }

关键点

  • User.prototype 是一个对象,用于存储所有实例共享的属性和方法。
  • User.prototype.constructor === User(始终成立)。

2. 实例与原型链

2.1 创建实例

使用 new 调用构造函数时,会创建一个新对象(实例),并设置其 __proto__ 指向构造函数的 prototype

javascript 复制代码
const user1 = new User("Alice");
const user2 = new User("Bob");

2.2 实例的 __proto__

实例的 __proto__(即 [[Prototype]])指向构造函数的 prototype

javascript 复制代码
console.log(user1.__proto__ === User.prototype); // true
console.log(user2.__proto__ === User.prototype); // true

图解关系

javascript 复制代码
user1.__proto__ → User.prototype
user2.__proto__ → User.prototype
User.prototype.constructor → User

3. 原型三角关系

3.1 核心等式

  1. 实例的 __proto__ 指向构造函数的 prototype

    javascript 复制代码
    user1.__proto__ === User.prototype; // true
  2. 原型对象的 constructor 指向构造函数

    javascript 复制代码
    User.prototype.constructor === User; // true
  3. 原型对象的 __proto__ 指向 Object.prototype (因为 User.prototype 本身是一个对象):

    javascript 复制代码
    User.prototype.__proto__ === Object.prototype; // true
  4. Object.prototype 是原型链的终点

    javascript 复制代码
    Object.prototype.__proto__ === null; // true

3.2 完整原型链

javascript 复制代码
user1 → User.prototype → Object.prototype → null

4. 代码验证

4.1 在原型上添加方法

所有实例共享原型上的方法:

javascript 复制代码
User.prototype.greet = function() {
    console.log(`Hello, my name is ${this.name}`);
};

user1.greet(); // "Hello, my name is Alice"
user2.greet(); // "Hello, my name is Bob"

4.2 检查原型关系

javascript 复制代码
console.log(user1 instanceof User); // true(检查原型链)
console.log(User.prototype.isPrototypeOf(user1)); // true(直接检查)

5. 常见误区澄清

5.1 误区 1:user1.prototype 存在

  • 错误 :认为实例有 prototype 属性。
  • 正确 :只有函数才有 prototype,实例的 __proto__ 指向构造函数的 prototype

5.2 误区 2:混淆 __proto__prototype

  • __proto__ 是实例的属性(非标准,但所有浏览器支持)。
  • prototype 是函数的属性,用于构造实例的原型。

5.3 误区 3:直接修改 __proto__

虽然可以修改 __proto__,但推荐使用 Object.create()Object.setPrototypeOf()

javascript 复制代码
// 不推荐(性能差)
user1.__proto__ = { ... };

// 推荐方式
const newProto = { ... };
Object.setPrototypeOf(user1, newProto);

6. 实际应用:继承

6.1 原型继承示例

javascript 复制代码
function Admin(name, role) {
    User.call(this, name); // 继承属性
    this.role = role;
}

// 设置原型链
Admin.prototype = Object.create(User.prototype);
Admin.prototype.constructor = Admin;

// 添加方法
Admin.prototype.manage = function() {
    console.log(`${this.name} is managing as ${this.role}`);
};

const admin = new Admin("Charlie", "Admin");
admin.greet(); // 继承自 User.prototype
admin.manage(); // "Charlie is managing as Admin"

6.2 ES6 Class 语法糖

javascript 复制代码
class User {
    constructor(name) {
        this.name = name;
    }
    greet() {
        console.log(`Hello, my name is ${this.name}`);
    }
}

class Admin extends User {
    constructor(name, role) {
        super(name);
        this.role = role;
    }
    manage() {
        console.log(`${this.name} is managing as ${this.role}`);
    }
}

7. 总结

  1. 构造函数 :通过 new 创建实例,并关联 prototype
  2. 原型对象 :存储共享属性和方法,constructor 指向构造函数。
  3. 实例 :通过 __proto__ 访问原型链。
  4. 原型链实例 → 构造函数.prototype → Object.prototype → null

关键等式

javascript 复制代码
instance.__proto__ === Constructor.prototype;
Constructor.prototype.constructor === Constructor;
Constructor.prototype.__proto__ === Object.prototype;
Object.prototype.__proto__ === null;

理解这些关系后,你可以更高效地使用 JavaScript 的面向对象编程,并避免常见的原型继承陷阱! 🚀

相关推荐
旺仔小拳头..5 分钟前
HTML的布局—— DIV 与 SPAN
前端·html
T___T5 分钟前
从原生 CSS 到 Stylus:用弹性布局实现交互式图片面板
前端·css
Zyx20075 分钟前
Stylus 进阶:从“能用”到“精通”,打造企业级 CSS 架构(下篇)
前端·css
黄毛火烧雪下7 分钟前
Angular 入门项目
前端·angular
用户4099322502128 分钟前
快速入门Vue3,插值、动态绑定和避坑技巧你都搞懂了吗?
前端·ai编程·trae
CondorHero9 分钟前
Environment 源码解读
前端
残冬醉离殇11 分钟前
别再傻傻分不清!从axios、ElementPlus深入理解SDK与API的区别
前端
CodeSheep20 分钟前
稚晖君官宣,全球首个0代码机器人创作平台来了!
前端·后端·程序员
向上的车轮23 分钟前
Actix Web 入门与实战
前端·rust·actix web
Every exam must be30 分钟前
10.27 JS学习12
开发语言·javascript·学习