Super 关键字:连接子类与父类的桥梁
当你在建造一座大楼,需要在现有地基上继续施工时,你不会完全从零开始,而是会利用已有的基础结构,然后在此基础上添加新的楼层和功能。在 JavaScript 的类继承中,super 关键字就扮演着这样的角色——它让子类能够调用和利用父类的功能。
super 是 ES6 引入的一个重要关键字,它既是子类与父类之间的通信桥梁,也是实现代码复用和功能扩展的核心机制。掌握 super 的使用,意味着你能够构建更加优雅和高效的面向对象程序。
Super 的两种使用形式
super 关键字有两种主要的使用形式:作为函数调用和作为对象使用。
1. Super 作为函数调用
当 super 作为函数使用时,它用于调用父类的构造函数:
javascript
class Animal {
constructor(name, species) {
this.name = name;
this.species = species;
console.log(`Animal 构造函数:创建了一只 ${species},名字叫 ${name}`);
}
eat() {
console.log(`${this.name} 正在吃东西`);
}
}
class Dog extends Animal {
constructor(name, breed) {
// 调用父类构造函数
super(name, "犬类");
this.breed = breed;
console.log(`Dog 构造函数:品种是 ${breed}`);
}
bark() {
console.log(`${this.name} 汪汪叫`);
}
}
const myDog = new Dog("旺财", "金毛");
// 输出:
// "Animal 构造函数:创建了一只 犬类,名字叫 旺财"
// "Dog 构造函数:品种是 金毛"
console.log(myDog);
// Dog {
// name: "旺财",
// species: "犬类",
// breed: "金毛"
// }关键规则:在子类构造函数中,super() 必须在使用 this 关键字之前调用:
javascript
class WrongExample extends Animal {
constructor(name, breed) {
this.breed = breed; // ❌ 错误:在 super() 之前使用 this
super(name, "犬类");
}
}
class CorrectExample extends Animal {
constructor(name, breed) {
super(name, "犬类"); // ✅ 正确:先调用 super()
this.breed = breed; // 然后使用 this
}
}2. Super 作为对象使用
当 super 作为对象使用时,它可以用来调用父类的方法:
javascript
class Vehicle {
constructor(speed) {
this.speed = speed;
}
move() {
console.log(`交通工具以 ${this.speed} km/h 的速度移动`);
}
stop() {
console.log("交通工具停止了");
this.speed = 0;
}
getInfo() {
return `速度:${this.speed} km/h`;
}
}
class Car extends Vehicle {
constructor(speed, brand) {
super(speed);
this.brand = brand;
}
move() {
// 调用父类方法并添加子类特有的逻辑
console.log(`${this.brand} 汽车启动了`);
super.move(); // 调用父类的 move 方法
}
getInfo() {
// 扩展父类方法
const baseInfo = super.getInfo(); // 调用父类 getInfo
return `${this.brand} 汽车 - ${baseInfo}`;
}
stop() {
// 完全重写父类方法
console.log(`${this.brand} 汽车刹车了`);
super.stop(); // 仍然调用父类停止逻辑
console.log("发动机熄火");
}
}
const myCar = new Car(120, "Toyota");
myCar.move();
// "Toyota 汽车启动了"
// "交通工具以 120 km/h 的速度移动"
console.log(myCar.getInfo());
// "Toyota 汽车 - 速度:120 km/h"
myCar.stop();
// "Toyota 汽车刹车了"
// "交通工具停止了"
// "发动机熄火"Super 在静态方法中的使用
super 也可以在静态方法中使用,此时它调用的是父类的静态方法:
javascript
class Database {
static connection = null;
static connect(config) {
this.connection = `连接到数据库: ${config.host}`;
console.log(this.connection);
return this.connection;
}
static disconnect() {
this.connection = null;
console.log("数据库连接已断开");
}
static getConnectionInfo() {
return this.connection || "未连接";
}
}
class MySQLDatabase extends Database {
static connect(config) {
console.log("正在初始化 MySQL 连接...");
const result = super.connect(config); // 调用父类静态方法
console.log("MySQL 连接配置完成");
return result;
}
static getConnectionInfo() {
const baseInfo = super.getConnectionInfo(); // 调用父类静态方法
return `MySQL - ${baseInfo}`;
}
static executeQuery(sql) {
if (!this.connection) {
console.log("请先连接数据库");
return;
}
console.log(`执行 SQL: ${sql}`);
}
}
MySQLDatabase.connect({ host: "localhost", port: 3306 });
// "正在初始化 MySQL 连接..."
// "连接到数据库: localhost"
// "MySQL 连接配置完成"
console.log(MySQLDatabase.getConnectionInfo());
// "MySQL - 连接到数据库: localhost"
MySQLDatabase.executeQuery("SELECT * FROM users");
// "执行 SQL: SELECT * FROM users"Super 的实际应用场景
1. UI 组件中的方法增强
javascript
class UIComponent {
constructor(element, options = {}) {
this.element = element;
this.options = { ...this.getDefaultOptions(), ...options };
this.isRendered = false;
this.eventListeners = new Map();
}
getDefaultOptions() {
return {
visible: true,
disabled: false,
className: "",
styles: {},
};
}
render() {
// 基础渲染逻辑
this.element.style.display = this.options.visible ? "block" : "none";
this.element.disabled = this.options.disabled;
this.element.className = this.options.className;
// 应用样式
Object.assign(this.element.style, this.options.styles);
this.isRendered = true;
console.log("基础组件渲染完成");
}
bindEvents() {
// 基础事件绑定
if (this.options.disabled) {
this.element.style.opacity = "0.6";
this.element.style.pointerEvents = "none";
}
}
show() {
this.options.visible = true;
this.element.style.display = "block";
}
hide() {
this.options.visible = false;
this.element.style.display = "none";
}
enable() {
this.options.disabled = false;
this.element.disabled = false;
this.element.style.opacity = "1";
this.element.style.pointerEvents = "auto";
}
disable() {
this.options.disabled = true;
this.element.disabled = true;
this.element.style.opacity = "0.6";
this.element.style.pointerEvents = "none";
}
destroy() {
// 清理事件监听器
this.eventListeners.forEach((handler, event) => {
this.element.removeEventListener(event, handler);
});
this.eventListeners.clear();
console.log("基础组件已销毁");
}
}
class Modal extends UIComponent {
getDefaultOptions() {
return {
...super.getDefaultOptions(), // 调用父类方法获取默认选项
title: "模态框",
content: "",
width: "auto",
height: "auto",
closable: true,
showOverlay: true,
};
}
render() {
// 调用父类基础渲染
super.render();
// 创建模态框结构
this.createOverlay();
this.createModalContent();
console.log("模态框渲染完成");
}
createOverlay() {
if (this.options.showOverlay) {
this.overlay = document.createElement("div");
this.overlay.className = "modal-overlay";
this.overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
`;
document.body.appendChild(this.overlay);
}
}
createModalContent() {
this.element.innerHTML = `
<div class="modal-header">
<h3>${this.options.title}</h3>
${
this.options.closable
? '<button class="modal-close">×</button>'
: ""
}
</div>
<div class="modal-body">
${this.options.content}
</div>
`;
// 设置模态框样式
Object.assign(this.element.style, {
position: "fixed",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: this.options.width,
height: this.options.height,
background: "white",
borderRadius: "8px",
boxShadow: "0 4px 20px rgba(0, 0, 0, 0.3)",
zIndex: "1001",
});
}
bindEvents() {
// 调用父类基础事件绑定
super.bindEvents();
// 添加模态框特有的事件
if (this.options.closable) {
const closeBtn = this.element.querySelector(".modal-close");
const closeHandler = () => this.hide();
closeBtn.addEventListener("click", closeHandler);
this.eventListeners.set("close", closeHandler);
if (this.overlay) {
const overlayHandler = () => this.hide();
this.overlay.addEventListener("click", overlayHandler);
this.eventListeners.set("overlay", overlayHandler);
}
// ESC 键关闭
const keyHandler = (e) => {
if (e.key === "Escape") {
this.hide();
}
};
document.addEventListener("keydown", keyHandler);
this.eventListeners.set("keydown", keyHandler);
}
}
show() {
super.show(); // 调用父类显示方法
if (this.overlay) {
this.overlay.style.display = "block";
}
// 禁止背景滚动
document.body.style.overflow = "hidden";
}
hide() {
super.hide(); // 调用父类隐藏方法
if (this.overlay) {
this.overlay.style.display = "none";
}
// 恢复背景滚动
document.body.style.overflow = "auto";
}
setContent(content) {
this.options.content = content;
const body = this.element.querySelector(".modal-body");
if (body) {
body.innerHTML = content;
}
}
setTitle(title) {
this.options.title = title;
const titleElement = this.element.querySelector(".modal-header h3");
if (titleElement) {
titleElement.textContent = title;
}
}
destroy() {
if (this.overlay && this.overlay.parentNode) {
this.overlay.parentNode.removeChild(this.overlay);
}
// 调用父类销毁方法
super.destroy();
console.log("模态框已销毁");
}
}
// 使用示例
const modal = new Modal(document.createElement("div"), {
title: "确认删除",
content: "您确定要删除这个项目吗?此操作不可撤销。",
width: "400px",
});
document.body.appendChild(modal.element);
modal.render();
modal.show();2. 数据模型中的方法继承
javascript
class BaseModel {
constructor(data = {}) {
this.id = data.id || this.generateId();
this.createdAt = data.createdAt || new Date();
this.updatedAt = data.updatedAt || new Date();
this.isDeleted = false;
this.validateAndAssign(data);
}
generateId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2, 9);
}
validateAndAssign(data) {
Object.keys(data).forEach((key) => {
if (this.isValidProperty(key, data[key])) {
this[key] = data[key];
}
});
}
isValidProperty(key, value) {
// 基础验证逻辑
return key !== "id" && key !== "createdAt" && key !== "updatedAt";
}
update(data) {
const oldData = { ...this };
this.validateAndAssign(data);
this.updatedAt = new Date();
console.log(`更新记录 ${this.id}:`, {
before: oldData,
after: this.toJSON(),
});
return this.save();
}
save() {
console.log("保存数据:", this.toJSON());
return Promise.resolve(this);
}
delete() {
this.isDeleted = true;
this.deletedAt = new Date();
console.log("删除记录:", this.id);
return Promise.resolve(true);
}
restore() {
this.isDeleted = false;
delete this.deletedAt;
console.log("恢复记录:", this.id);
return this.save();
}
toJSON() {
const result = {
id: this.id,
createdAt: this.createdAt,
updatedAt: this.updatedAt,
};
if (this.isDeleted) {
result.deletedAt = this.deletedAt;
}
return result;
}
static find(criteria = {}) {
console.log("查找记录:", criteria);
return Promise.resolve([]);
}
static findOne(criteria = {}) {
console.log("查找单个记录:", criteria);
return Promise.resolve(null);
}
}
class UserModel extends BaseModel {
constructor(data = {}) {
super(data);
this.role = data.role || "user";
this.loginCount = data.loginCount || 0;
this.lastLoginAt = data.lastLoginAt || null;
}
isValidProperty(key, value) {
// 调用父类验证
const baseValid = super.isValidProperty(key, value);
// 用户模型的特殊验证
const userValidations = {
email: (val) => typeof val === "string" && val.includes("@"),
username: (val) => typeof val === "string" && val.length >= 3,
age: (val) => Number.isInteger(val) && val >= 0 && val <= 150,
role: (val) => ["user", "admin", "moderator"].includes(val),
};
return baseValid && (!userValidations[key] || userValidations[key](value));
}
login() {
this.lastLoginAt = new Date();
this.loginCount++;
this.updatedAt = new Date();
console.log(`用户 ${this.username} 登录,第 ${this.loginCount} 次`);
return this.save();
}
logout() {
console.log(`用户 ${this.username} 登出`);
return Promise.resolve(this);
}
changeRole(newRole) {
const oldRole = this.role;
this.role = newRole;
this.updatedAt = new Date();
console.log(`用户 ${this.username} 角色从 ${oldRole} 变更为 ${newRole}`);
return this.save();
}
toJSON() {
// 调用父类方法获取基础数据
const baseJSON = super.toJSON();
return {
...baseJSON,
username: this.username,
email: this.email,
role: this.role,
loginCount: this.loginCount,
lastLoginAt: this.lastLoginAt,
};
}
static findByEmail(email) {
console.log("根据邮箱查找用户:", email);
return Promise.resolve(null);
}
static authenticate(email, password) {
console.log("用户认证:", email);
return Promise.resolve(null);
}
}
class AdminModel extends UserModel {
constructor(data = {}) {
super(data);
this.role = "admin";
this.permissions = data.permissions || [];
this.adminLevel = data.adminLevel || 1;
}
isValidProperty(key, value) {
// 调用父类验证
const userValid = super.isValidProperty(key, value);
// 管理员的特殊验证
const adminValidations = {
permissions: (val) => Array.isArray(val),
adminLevel: (val) => Number.isInteger(val) && val >= 1 && val <= 5,
};
return (
userValid && (!adminValidations[key] || adminValidations[key](value))
);
}
grantPermission(permission) {
if (!this.hasPermission(permission)) {
this.permissions.push(permission);
this.updatedAt = new Date();
console.log(`管理员 ${this.username} 获得权限: ${permission}`);
return this.save();
}
return Promise.resolve(this);
}
revokePermission(permission) {
const index = this.permissions.indexOf(permission);
if (index > -1) {
this.permissions.splice(index, 1);
this.updatedAt = new Date();
console.log(`管理员 ${this.username} 失去权限: ${permission}`);
return this.save();
}
return Promise.resolve(this);
}
hasPermission(permission) {
return this.permissions.includes(permission);
}
upgradeLevel(newLevel) {
const oldLevel = this.adminLevel;
this.adminLevel = Math.min(5, Math.max(1, newLevel));
this.updatedAt = new Date();
console.log(
`管理员 ${this.username} 等级从 ${oldLevel} 升级到 ${this.adminLevel}`
);
return this.save();
}
toJSON() {
// 调用父类方法
const userJSON = super.toJSON();
return {
...userJSON,
adminLevel: this.adminLevel,
permissions: [...this.permissions], // 复制数组避免外部修改
};
}
static findAdmins() {
console.log("查找所有管理员");
return Promise.resolve([]);
}
}
// 使用示例
const admin = new AdminModel({
username: "admin",
email: "[email protected]",
permissions: ["read", "write"],
adminLevel: 3,
});
admin.grantPermission("delete").then(() => {
console.log(admin.toJSON());
});Super 的常见陷阱和最佳实践
1. 避免在箭头函数中使用 super
javascript
class Parent {
method() {
console.log("Parent method");
}
}
class Child extends Parent {
constructor() {
super();
// ❌ 错误:箭头函数中的 super 不绑定到类的原型
this.arrowMethod = () => {
super.method(); // ReferenceError: super is not defined
};
// ✅ 正确:普通方法中的 super 正常工作
this.normalMethod = function () {
// this.constructor.prototype.__proto__.method.call(this);
// 但这种方式不推荐,建议使用类方法
};
}
// ✅ 正确:类方法中的 super
instanceMethod() {
super.method(); // 正常工作
}
}2. 注意 super 中的 this 绑定
javascript
class Parent {
constructor() {
this.name = "Parent";
}
getName() {
return this.name;
}
}
class Child extends Parent {
constructor() {
super();
this.name = "Child";
}
getParentName() {
// super 中的 this 指向当前子类实例
return super.getName(); // 返回 'Child',而不是 'Parent'
}
callParentMethodDirectly() {
// 直接调用父类方法,但 this 仍然指向子类实例
return Parent.prototype.getName.call(this); // 返回 'Child'
}
}
const child = new Child();
console.log(child.getParentName()); // "Child"
console.log(child.callParentMethodDirectly()); // "Child"3. 不要在静态方法中访问实例属性
javascript
class Parent {
constructor() {
this.instanceProperty = "instance";
}
static staticMethod() {
// ❌ 错误:静态方法中无法访问实例属性
console.log(this.instanceProperty); // undefined
}
}
class Child extends Parent {
static childStaticMethod() {
// ❌ 错误:super 在静态方法中无法访问实例属性
console.log(super.instanceProperty); // undefined
}
}4. 正确处理 super 调用的返回值
javascript
class Parent {
calculate(a, b) {
return a + b;
}
}
class Child extends Parent {
calculate(a, b, c) {
const parentResult = super.calculate(a, b); // 保存父类方法返回值
return parentResult * c; // 在父类结果基础上进行计算
}
}
const child = new Child();
console.log(child.calculate(2, 3, 4)); // 20 ( (2+3) * 4 )Super 与原型链的关系
理解 super 的工作原理有助于更好地使用它:
javascript
class Parent {
static staticMethod() {
return "Parent static";
}
instanceMethod() {
return "Parent instance";
}
}
class Child extends Parent {
static staticMethod() {
return super.staticMethod() + " -> Child static";
}
instanceMethod() {
return super.instanceMethod() + " -> Child instance";
}
}
// super 在静态方法中的行为
console.log(Child.staticMethod());
// 相当于:Object.getPrototypeOf(Child).staticMethod.call(this)
// super 在实例方法中的行为
const child = new Child();
console.log(child.instanceMethod());
// 相当于:Object.getPrototypeOf(Child.prototype).instanceMethod.call(this)
console.log(Object.getPrototypeOf(Child) === Parent); // true
console.log(Object.getPrototypeOf(Child.prototype) === Parent.prototype); // true总结
super 关键字是 JavaScript 类继承体系中不可或缺的重要工具。通过掌握这些概念和技术,你可以:
- 正确调用父类构造函数:使用
super()初始化继承的属性 - 重用父类方法:通过
super.method()调用并扩展现有功能 - 处理静态方法继承:在静态上下文中正确使用
super - 避免常见陷阱:理解
super的工作机制和限制
super 的使用不仅仅是语法糖,它体现了面向对象编程中"继承"和"扩展"的核心思想。合理使用 super 可以让你的代码更加模块化、可维护,并且能够有效地复用现有代码。
在实际开发中,始终记住:super 是连接子类与父类的桥梁,它让继承关系变得更加清晰和强大。掌握好 super 的使用,你就掌握了 JavaScript 面向对象编程的精髓。