摘要:
你是否曾被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操作符的四步魔法:
- 创建空对象
{} - 绑定原型:
空对象.__proto__ = 构造函数.prototype - 执行构造函数(将this指向新对象)
- 返回新对象(若构造函数未返回对象)
痛点暴露:
// 每个实例都创建新函数!内存浪费!
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
三、原型链:继承的终极奥义
查找机制:当访问对象属性时
- 先在自身属性查找
- 找不到则通过
__proto__向上查找 - 直到
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}正在学习`);
}
}
背后原理:
extends关键字 → 实现寄生组合继承super()→ 调用父类构造函数- 类方法 → 挂载到原型对象
结语与行动号召
🎯 现在你已掌握JS面向对象的核心机制!
1️⃣ 点赞支持硬核技术解析!
2️⃣ 收藏构建你的JS知识宝库!
3️⃣ 关注获取系列更新通知!
💡 下篇预告:《this全面解析:动态作用域的终极指南》—— 揭秘this绑定的四大规则!
🚀 你的每一次互动,都是我持续创作的能量源泉! 🚀