JavaScript原型链全景解析:从构造函数到继承的终极指南

145 阅读1分钟

摘要:

你是否曾被prototype__proto__绕晕?是否在实现继承时踩过坑?本文将带你拨开迷雾,从new操作符的魔法开始,层层拆解原型链机制。通过手绘原型链示意图+6种继承模式对比,彻底掌握JS面向对象编程的精髓!


正文内容

一、构造函数:对象的出生证明

本质:用于创建对象的函数(通常首字母大写)

function Person(name) {
  this.name = name;
  this.say = function() {
    console.log(`我是${this.name}`);
  };
}

const person1 = new Person("小明");
person1.say(); // 输出"我是小明"

new操作符的四步魔法

  1. 创建空对象 {}
  2. 绑定原型:空对象.__proto__ = 构造函数.prototype
  3. 执行构造函数(将this指向新对象)
  4. 返回新对象(若构造函数未返回对象)

痛点暴露

// 每个实例都创建新函数!内存浪费!
console.log(person1.say === new Person("小红").say); // false

二、原型对象:共享能力的基因库

解决方案:将公共方法放入原型

function Person(name) {
  this.name = name;
}

// 方法放在原型上实现共享
Person.prototype.say = function() {
  console.log(`我是${this.name}`);
};

const p1 = new Person("小明");
const p2 = new Person("小红");
console.log(p1.say === p2.say); // true!共享同一函数

核心关系图

graph LR
  A[实例 p1] -- __proto__ --> B[Person.prototype]
  B -- constructor --> C[Person构造函数]
  C -- prototype --> B

三、原型链:继承的终极奥义

查找机制:当访问对象属性时

  1. 先在自身属性查找
  2. 找不到则通过__proto__向上查找
  3. 直到Object.prototype(终点为null)

手动实现继承

function Student(name, grade) {
  Person.call(this, name); // 继承属性
  this.grade = grade;
}

// 关键!设置原型链
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student; // 修复构造函数指向

// 添加子类方法
Student.prototype.study = function() {
  console.log(`${this.name}正在学习`);
};

完整原型链图示

graph BT
  A[stu实例] -- __proto__ --> B[Student.prototype]
  B -- __proto__ --> C[Person.prototype]
  C -- __proto__ --> D[Object.prototype]
  D -- __proto__ --> E[null]

四、六大继承模式全面对比

模式关键代码优点缺点
原型链继承Child.prototype = new Parent()简单父类属性被所有子类实例共享
构造函数继承function Child() { Parent.call(this) }避免共享属性无法继承父类原型方法
组合继承构造函数+原型链双管齐下最常用两次调用父类构造函数
原型式继承Object.create()轻量同原型链继承缺陷
寄生继承工厂函数+增强对象不破坏原型链方法复用性差
寄生组合继承Child.prototype = Object.create(Parent.prototype)完美解决所有问题代码稍复杂

现代最佳实践(寄生组合继承)

function inherit(Child, Parent) {
  // 1. 创建父类原型副本
  const prototype = Object.create(Parent.prototype);
  // 2. 修复constructor指向
  prototype.constructor = Child;
  // 3. 挂载到子类原型
  Child.prototype = prototype;
}

// 使用示例
function Parent() {}
function Child() {
  Parent.call(this); // 继承实例属性
}
inherit(Child, Parent); // 继承原型方法

五、ES6 class语法糖揭秘

class本质:构造函数的语法包装

class Person {
  constructor(name) {
    this.name = name;
  }

  say() { // 自动放入原型
    console.log(`我是${this.name}`);
  }
}

// 等同于
function Person(name) {
  this.name = name;
}
Person.prototype.say = function() { ... };

继承实现

class Student extends Person {
  constructor(name, grade) {
    super(name); // 必须调用!
    this.grade = grade;
  }

  study() {
    console.log(`${this.name}正在学习`);
  }
}

背后原理

  1. extends关键字 → 实现寄生组合继承
  2. super() → 调用父类构造函数
  3. 类方法 → 挂载到原型对象

结语与行动号召

🎯 现在你已掌握JS面向对象的核心机制!
1️⃣ 点赞支持硬核技术解析!
2️⃣ 收藏构建你的JS知识宝库!
3️⃣ 关注获取系列更新通知!
💡 下篇预告:《this全面解析:动态作用域的终极指南》—— 揭秘this绑定的四大规则!

🚀 你的每一次互动,都是我持续创作的能量源泉! 🚀