面试题答案
一键面试设计泛型接口实现跨模块复用
- 定义泛型接口:
- 首先,确定操作逻辑中共性的部分,并将涉及到不同类型数据的部分定义为泛型。例如,如果这些操作逻辑主要是对数据的读取、处理和返回,可以定义如下泛型接口:
interface DataProcessor<T> {
readData(): T;
processData(data: T): T;
returnProcessedData(): T;
}
- 这里 `<T>` 是泛型参数,代表不同类型的数据。每个模块可以根据自身数据类型,实现这个泛型接口。
2. 模块实现泛型接口: - 假设有两个模块,一个处理字符串数据,一个处理数字数据。
class StringProcessor implements DataProcessor<string> {
private data: string;
constructor(data: string) {
this.data = data;
}
readData(): string {
return this.data;
}
processData(data: string): string {
return data.toUpperCase();
}
returnProcessedData(): string {
return this.processData(this.readData());
}
}
class NumberProcessor implements DataProcessor<number> {
private data: number;
constructor(data: number) {
this.data = data;
}
readData(): number {
return this.data;
}
processData(data: number): number {
return data * 2;
}
returnProcessedData(): number {
return this.processData(this.readData());
}
}
- 跨模块复用:
- 其他模块可以通过使用
DataProcessor
泛型接口,以统一的方式调用不同类型数据的处理逻辑。
- 其他模块可以通过使用
function useProcessor<T>(processor: DataProcessor<T>) {
const result = processor.returnProcessedData();
console.log(result);
}
const stringProcessor = new StringProcessor('hello');
const numberProcessor = new NumberProcessor(5);
useProcessor(stringProcessor);
useProcessor(numberProcessor);
可能出现的性能问题
- 类型检查开销:TypeScript 的类型检查在编译时进行,但一些运行时检查(如
instanceof
等操作涉及类型断言时)可能带来额外开销。当在泛型接口实现中频繁进行类型相关操作时,会影响性能。 - 装箱和拆箱:在使用泛型时,如果涉及到基本类型(如
number
、string
、boolean
)和其对应的包装类型(如Number
、String
、Boolean
),可能会发生装箱(将基本类型转换为包装类型)和拆箱(将包装类型转换为基本类型)操作,这会带来性能损耗。 - 函数调用开销:通过泛型接口实现的复用,可能导致更多的函数间接调用。例如,在
useProcessor
函数中调用processor.returnProcessedData()
,相比直接调用具体实现类的方法,会有额外的函数调用开销。
利用TypeScript特性进行性能优化
- 避免不必要的类型断言:尽量减少在运行时进行类型断言操作。如果可以在编译时通过类型推断确定类型,就避免使用
as
关键字进行类型断言。例如,在上述实现中,如果能通过泛型参数明确类型,就不需要额外的类型断言。 - 注意基本类型使用:尽量直接使用基本类型,避免不必要的装箱和拆箱。在定义泛型接口和实现类时,确保正确使用基本类型,而不是包装类型。例如,使用
number
而不是Number
。 - 内联函数:对于简单的操作逻辑,可以使用内联函数来减少函数调用开销。例如,如果
processData
方法只是简单的计算,可以将其定义为内联函数,在returnProcessedData
中直接计算,而不是通过单独的函数调用。
class NumberProcessor implements DataProcessor<number> {
private data: number;
constructor(data: number) {
this.data = data;
}
readData(): number {
return this.data;
}
returnProcessedData(): number {
return this.readData() * 2; // 内联计算,避免单独的processData函数调用
}
}