深入理解 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 的面向对象编程,并避免常见的原型继承陷阱! 🚀

相关推荐
mCell4 小时前
GSAP ScrollTrigger 详解
前端·javascript·动效
gnip4 小时前
Node.js 子进程:child_process
前端·javascript
excel7 小时前
为什么在 Three.js 中平面能产生“起伏效果”?
前端
excel8 小时前
Node.js 断言与测试框架示例对比
前端
天蓝色的鱼鱼9 小时前
前端开发者的组件设计之痛:为什么我的组件总是难以维护?
前端·react.js
codingandsleeping9 小时前
使用orval自动拉取swagger文档并生成ts接口
前端·javascript
石金龙10 小时前
[译] Composition in CSS
前端·css
白水清风10 小时前
微前端学习记录(qiankun、wujie、micro-app)
前端·javascript·前端工程化
Ticnix10 小时前
函数封装实现Echarts多表渲染/叠加渲染
前端·echarts
用户221520442780010 小时前
new、原型和原型链浅析
前端·javascript