TypeScript类静态变量在方法内初始化是否为不良实践及优化咨询
问题解答
基础示例问题分析
示例中的第一个Foo类写法确实属于不良实践,核心问题不是突变本身,而是无意义的全局共享状态突变:
- 静态属性
bar挂载在Foo类本身,属于全局共享状态,所有对Foo.fooBar的调用都会修改同一个属性值 - 多场景调用
fooBar传入不同值时,后执行的逻辑会覆盖前序的属性值,若后续新增其他依赖bar的静态方法,会出现预期外的逻辑错误 - 并发场景下该问题会被放大,无法保证状态一致性
单调用场景const foobar = Foo.fooBar('bar');不会直观暴露问题,但该写法从架构层面留下了可预见的风险,属于不良实践。
Service场景优化方案
为精简代码避免重复声明变量的初衷合理,但当前用静态属性存储uri的写法仍存在隐患:
- 若后续给
BooksService新增其他请求方法(如按ID查询单本图书的getBookById),不同方法给this.uri赋不同值时,会出现状态覆盖问题 - 异步请求场景下,先后发起两个不同请求,容易出现uri被覆盖、请求发送到错误地址的问题
- 不利于单元测试,测试时需要手动修改静态属性值,测试完成后还要复原,增加冗余成本
可根据业务需求选择对应最佳实践:
- uri为固定常量场景
直接在声明静态属性时完成初始化,不需要在方法内修改:
export class BooksService extends HttpService { // 直接赋值,全局唯一不可修改 private static uri = config.api.booksUri; public static async getBooks(): Promise<any> { const options = { method: 'GET', headers: { ...headers.headers, } }; return await fetch(this.uri, options).then(this.onResponse); } }
既满足精简代码的需求,也完全避免了状态突变的问题。
- uri需要根据方法参数动态拼接场景
抽离独立的私有静态方法生成uri,不使用共享的静态属性存储:
export class BooksService extends HttpService { // 抽离通用uri生成方法 private static getUri(bookId?: string): string { const baseUri = config.api.booksUri; return bookId ? `${baseUri}/${bookId}` : baseUri; } public static async getBooks(): Promise<any> { const options = { method: 'GET', headers: { ...headers.headers, } }; // 直接调用方法获取uri,无共享状态 return await fetch(this.getUri(), options).then(this.onResponse); } // 新增的按ID查询方法可复用 public static async getBookById(bookId: string): Promise<any> { const options = { method: 'GET', headers: { ...headers.headers, } }; return await fetch(this.getUri(bookId), options).then(this.onResponse); } }
该写法既避免了重复定义uri的冗余代码,也完全消除了共享状态突变的风险。
总结
突变本身不是必须完全禁止的行为,但无约束的共享状态突变一定是不良实践,会大幅降低代码的可预测性、可维护性和可测试性,能避免时应尽量避免。
内容的提问来源于stack exchange,提问作者Joseph Freeman
相关产品推荐
相关产品推荐

