Angular循环内实例化对象触发Green Software扫描报错求助
问题解决:Green Software扫描检测到Angular循环内实例化对象Bug
问题背景
Green Software扫描工具检测到代码中的Bug,报错行位于循环内实例化AttachmentItem的语句:
const attachment: AttachmentItem = new AttachmentItem(uuid(), file);
相关完整代码如下:
文件添加队列方法
public addFilesToQueue(files: FileList | any): void { const addedFiles: AttachmentItem[] = []; for (let iFileId = 0; iFileId < files.length; iFileId++) { const file = files.item(iFileId); const attachment: AttachmentItem = new AttachmentItem(uuid(), file); if (!attachment || !attachment.file) { return; } this.attachments.push(attachment); } this.messageBusService .addMessage<AttachmentItem[]>(WebMessageBusChannelNameConstant.attachmentTableListFile, WebMessageBusEventNameConstant.updateAttachmentTableListFile, this.attachments); for (const availableFile of this.attachments) { this.buildUploadRequest(availableFile).pipe( switchMap((httpRequest: HttpRequest<any>) => { return this.httpClient.request(httpRequest); }), map(httpEvent => { return availableFile; }) ).subscribe(); } }
AttachmentItem类定义
export declare class AttachmentItem { id: string; fileName: string; fileType: string; referenceType: string; referenceId: string; documentType: string; updatedAt: string; updatedBy: string; file: File; status: AttachmentItemUploadStatuses; private _progress; private _canceller; progress: number; readonly canceller: Subject<boolean>; constructor(id: string, file: File); }
问题分析
Green Software工具的报错核心在于循环内频繁实例化对象可能带来的性能问题:
- 每次循环创建新的
AttachmentItem实例,会增加垃圾回收(GC)的压力,批量添加大量文件时易产生内存碎片。 - 若
AttachmentItem构造函数内部存在复杂初始化逻辑(比如初始化Subject),循环内重复执行会降低代码执行效率。
另外原代码存在隐藏问题:if (!attachment || !attachment.file) { return; }中的return会直接终止整个循环,导致后续文件无法处理,应改为continue跳过当前无效文件。
解决方案
方案1:优化类初始化逻辑,延迟非必要属性加载
修改AttachmentItem类,将非必要属性改为懒加载,减少构造函数的初始化开销:
export class AttachmentItem { id: string; fileName: string; fileType: string; referenceType: string; referenceId: string; documentType: string; updatedAt: string; updatedBy: string; file: File; status: AttachmentItemUploadStatuses; private _progress = 0; private _canceller?: Subject<boolean>; // 改为可选属性 progress: number; get canceller(): Subject<boolean> { // 懒加载Subject,仅在需要时初始化 if (!this._canceller) { this._canceller = new Subject<boolean>(); } return this._canceller; } constructor(id: string, file: File) { this.id = id; this.file = file; this.fileName = file.name; this.fileType = file.type; this.status = AttachmentItemUploadStatuses.PENDING; // 仅初始化必要属性,其他属性按需赋值 } }
方案2:修正循环逻辑,优化上传请求处理
修改循环内的判断逻辑,用continue替代return,同时优化上传请求的订阅方式:
import { forkJoin } from 'rxjs'; public addFilesToQueue(files: FileList | any): void { const addedFiles: AttachmentItem[] = []; for (let iFileId = 0; iFileId < files.length; iFileId++) { const file = files.item(iFileId); if (!file) continue; // 提前跳过无效文件 const attachment: AttachmentItem = new AttachmentItem(uuid(), file); if (!attachment) continue; this.attachments.push(attachment); addedFiles.push(attachment); } this.messageBusService .addMessage<AttachmentItem[]>(WebMessageBusChannelNameConstant.attachmentTableListFile, WebMessageBusEventNameConstant.updateAttachmentTableListFile, this.attachments); // 用forkJoin合并请求,避免多次独立subscribe forkJoin( this.attachments.map(availableFile => this.buildUploadRequest(availableFile).pipe( switchMap((httpRequest: HttpRequest<any>) => this.httpClient.request(httpRequest)), map(() => availableFile) ) ) ).subscribe(); }
方案3:对象池复用(适用批量重复场景)
如果存在批量处理同类型文件的场景,可通过对象池复用AttachmentItem实例(仅作参考,因每个实例绑定不同File,适用性有限):
private attachmentPool: AttachmentItem[] = []; private getAttachmentItem(id: string, file: File): AttachmentItem { if (this.attachmentPool.length > 0) { const item = this.attachmentPool.pop()!; item.id = id; item.file = file; item.fileName = file.name; item.fileType = file.type; item.status = AttachmentItemUploadStatuses.PENDING; // 重置其他属性 return item; } return new AttachmentItem(id, file); } // 循环内使用对象池获取实例 const attachment: AttachmentItem = this.getAttachmentItem(uuid(), file);
总结
优先选择方案1+方案2的组合:通过懒加载减少构造函数开销,修正循环终止逻辑,同时优化上传请求的订阅方式,既解决Green Software的扫描告警,又提升代码的健壮性和性能。
内容的提问来源于stack exchange,提问作者Thanh
相关产品推荐
相关产品推荐

