You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 07:27:08