JS中Class继承:如何正确复写父类方法并修改入参
在JavaScript的面向对象编程中,Class继承是非常常见的场景。当子类需要复用父类的逻辑,同时又要对父类的方法进行定制时,就涉及到父类方法的复写以及入参修改的问题。本文将详细介绍正确的实现方式,以及需要注意的边界情况。
基础回顾:Class继承的基本语法
ES6引入的Class语法本质是原型继承的语法糖,通过extends关键字可以实现子类对父类的继承,使用super关键字可以调用父类的构造函数或者方法。首先看一个基础的父类示例:
class Parent {
constructor(name) {
this.name = name;
}
// 父类的基础方法,接收两个参数并返回拼接结果
greet(prefix, suffix) {
return `${prefix}${this.name}${suffix}`;
}
}上述Parent类有一个greet方法,接收prefix和suffix两个入参,返回拼接后的字符串。
复写父类方法并修改入参的正确方式
当子类需要复写父类的greet方法,同时修改入参逻辑时,核心思路是:在子类方法中先处理自己的入参逻辑,再通过super.方法名()调用父类的同名方法,传入处理后的参数。以下是具体实现:
class Child extends Parent {
constructor(name, age) {
// 先调用父类构造函数,传入子类需要的参数
super(name);
this.age = age;
}
// 复写父类的greet方法,新增age参数,修改入参逻辑
greet(prefix, suffix, showAge) {
// 子类自己的入参处理逻辑:如果showAge为true,修改前缀加入年龄信息
let newPrefix = prefix;
if (showAge) {
newPrefix = `${prefix}[${this.age}岁]`;
}
// 调用父类的greet方法,传入处理后的参数,也可以补充子类特有的参数处理
const parentResult = super.greet(newPrefix, suffix);
// 可以在父类返回结果的基础上做进一步处理
return `${parentResult},欢迎来到https://www.ipipp.com`;
}
}上述代码中,Child类复写了greet方法,新增了showAge入参,先根据showAge的值修改了prefix参数,再通过super.greet调用父类方法传入处理后的参数,最后对返回结果做了补充。我们测试一下效果:
const parent = new Parent('张三');
console.log(parent.greet('你好,', '!'));
// 输出:你好,张三!
const child = new Child('李四', 18);
console.log(child.greet('你好,', '!', true));
// 输出:你好,[18岁]李四!,欢迎来到https://www.ipipp.com
console.log(child.greet('你好,', '!', false));
// 输出:你好,李四!,欢迎来到https://www.ipipp.com需要注意的关键问题
1. super的使用限制
在子类的构造函数中,必须先调用super()才能使用this,否则会抛出引用错误。而在子类的方法中,super.父类方法名()的调用没有顺序限制,只要是在子类方法的逻辑中合适的位置即可。
2. 入参的兼容性处理
如果子类复写方法时修改了入参的数量或者含义,建议尽量保持和父类方法的兼容,避免破坏原有的调用逻辑。比如如果父类方法支持两个参数,子类新增参数可以设置为可选参数,这样原有调用父类方法的代码在子类实例上也能正常运行:
class CompatibleChild extends Parent {
greet(prefix, suffix, showAge = false) {
// 即使不传showAge,也能正常调用,兼容父类的调用方式
let newPrefix = prefix;
if (showAge && this.age) {
newPrefix = `${prefix}[${this.age}岁]`;
}
return super.greet(newPrefix, suffix);
}
}
const compatibleChild = new CompatibleChild('王五', 20);
console.log(compatibleChild.greet('你好,', '!')); // 正常运行,输出:你好,王五!3. 不要直接修改父类的原型方法
有些开发者可能会尝试直接修改Parent.prototype.greet来实现复写,这种方式会修改所有父类实例的行为,不符合继承的预期,也不利于代码的维护,因此不推荐使用。
复杂场景:修改入参并复用父类逻辑
如果父类的入参比较复杂,子类需要对其中的部分参数做转换后再传入,也可以先解析父类的入参结构,再构造新的参数传入。例如父类方法接收一个配置对象作为入参:
class ConfigParent {
constructor(name) {
this.name = name;
}
// 父类方法接收配置对象入参
formatInfo(config) {
const { prefix = '', suffix = '', upperCase = false } = config;
let result = `${prefix}${this.name}${suffix}`;
if (upperCase) {
result = result.toUpperCase();
}
return result;
}
}
class ConfigChild extends ConfigParent {
// 复写方法,修改入参逻辑:新增timestamp参数,补充到prefix中
formatInfo(config) {
const { prefix = '', suffix = '', upperCase = false, addTimestamp = false } = config;
let newPrefix = prefix;
if (addTimestamp) {
newPrefix = `${new Date().toLocaleDateString()}-${prefix}`;
}
// 构造新的配置对象传入父类方法
return super.formatInfo({
prefix: newPrefix,
suffix,
upperCase
});
}
}
const configChild = new ConfigChild('赵六');
console.log(configChild.formatInfo({
prefix: '用户:',
suffix: ',信息已同步',
addTimestamp: true
}));
// 输出示例:2024/5/20-用户:赵六,信息已同步这种场景下,子类先解构父类的入参配置,再根据自己的逻辑修改部分配置项,最后构造符合父类要求的参数传入,既复用了父类的逻辑,又实现了入参的定制修改。
总结
在JS的Class继承中复写父类方法并修改入参,核心步骤为:
子类通过同名方法复写父类方法
在子类方法中先处理自己的入参逻辑,得到符合父类要求的参数
通过
super.父类方法名(处理后的参数)调用父类方法,复用原有逻辑可根据需要对父类返回的结果做进一步处理,再返回最终值
同时注意入参的兼容性、super的使用规则,避免直接修改父类原型,就能规范地实现方法复写和入参修改的需求。