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

GraalVM中更新单个数组却同步修改另一数组的问题排查

在GraalVM中修改一个数组导致另一个数组同步变更的问题

在GraalVM环境下,我遇到一个异常问题:仅更新results数组时,originalResults数组也会同步被修改。我尝试过使用扩展运算符复制原数组、通过循环单独填充数组,但问题仍未解决。

相关代码

调用代码(code1)

s.print("sys_imported_saved_configuration_id: " + sys_imported_saved_configuration_id);
const sys_imported_saved_configuration_data = new SysRecord({
    sys_table_name: 'sys_imported_saved_configuration_data',
    where: [
        [{
            "sys_column": "sys_imported_saved_configuration_id",
            "sys_operator": "=",
            "sys_value": sys_imported_saved_configuration_id
        }]
    ],
    select_columns: ['sys_id', 'sys_data', 'sys_operation']
});

const is_data_available = await sys_imported_saved_configuration_data.query();

SysRecord类代码(code2)

class SysRecord {
    constructor({ sys_table_name, select_columns = null, where = null, where_sys_id = null, sort_column = null, sort_direction = null }) {
        this.sys_table_name = sys_table_name;
        this.select_columns = select_columns || [];
        this.where = where || {};
        this.where_sys_id = where_sys_id;
        this.sort_column = sort_column;
        this.sort_direction = sort_direction;
        this.results = [];
        this.currentIndex = -1;
        this.originalResults = []; // 用于追踪原始记录
    }

    toMap() {
        const queryParams = {};
        if (this.sys_table_name) queryParams['sys_table_name'] = this.sys_table_name;
        if (this.select_columns.length > 0) queryParams['select_columns'] = JSON.stringify(this.select_columns);
        if (Object.keys(this.where).length > 0) queryParams['where'] = JSON.stringify(this.where);
        if (this.where_sys_id) queryParams['where_sys_id'] = this.where_sys_id;
        if (this.sort_column) queryParams['sort_column'] = this.sort_column;
        if (this.sort_direction) queryParams['sort_direction'] = this.sort_direction;
        return queryParams;
    }

    async query() {
        s.print("\n\nquery()");
        // 检查并添加sys_id和sys_table_name到select_columns(如果不存在)
        if (!this.select_columns.includes('sys_id')) {
            this.select_columns.push('sys_id');
        }
        if (!this.select_columns.includes('sys_table_name')) {
            this.select_columns.push('sys_table_name');
        }

        // 执行查询
        const records = await s.get(this.toMap());
        s.print("RECORDS: " + records);

        for (var record of records) {
            s.print("record: " + record);

            this.originalResults.push(record);
            this.results.push(record);
        }

        // 曾尝试的方案(无效)
        // this.originalResults = [...records];
        // this.results = [...records];

        s.print("originalResults: " + this.originalResults);
        s.print("results: " + this.results);

        this.currentIndex = 0;
        this.set('sys_create', true);

        s.print("\noriginalResults2: " + this.originalResults);
        s.print("results2: " + this.results);

        return this.results.length > 0;
    }

    get(fieldName) {
        if (this.currentIndex >= 0 && this.currentIndex < this.results.length) {
            return this.results[this.currentIndex][fieldName];
        }
        return null;
    }

    set(fieldName, value) {
        // s.print("set called with " + fieldName + " :: " + value + " at index = " + this.currentIndex);
        if (this.currentIndex >= 0 && this.currentIndex < this.results.length) {
            this.results[this.currentIndex][fieldName] = value;
        }
    }
}

问题原因

这不是GraalVM的特殊问题,而是JavaScript对象引用的特性:

  • records数组中的元素是对象,无论是用push(record)还是扩展运算符[...records],都是浅拷贝——只复制了数组的结构,两个数组中的元素依然指向同一个对象引用。
  • 当你调用set方法修改results数组中对象的属性时,实际上是修改了底层的对象本身,originalResults数组中对应的对象自然也会同步变化。

解决方案

需要对每个对象进行深拷贝,确保两个数组中的对象是完全独立的实例。可以根据对象的结构选择以下方式:

方案1:使用JSON序列化/反序列化(适用于无循环引用的对象)

修改query方法中的循环部分:

for (const record of records) {
    s.print("record: " + record);
    // 深拷贝单个对象
    const originalCopy = JSON.parse(JSON.stringify(record));
    const resultCopy = JSON.parse(JSON.stringify(record));
    this.originalResults.push(originalCopy);
    this.results.push(resultCopy);
}

方案2:手动拷贝对象属性(更安全,适用于已知结构的对象)

如果明确知道record的属性,可以手动复制避免序列化的潜在问题:

for (const record of records) {
    s.print("record: " + record);
    const recordCopy = {
        sys_id: record.sys_id,
        sys_data: record.sys_data,
        sys_operation: record.sys_operation,
        sys_table_name: record.sys_table_name
    };
    this.originalResults.push({...recordCopy}); // 再浅拷贝一次确保独立
    this.results.push(recordCopy);
}

方案3:使用结构化克隆(现代环境支持)

如果运行环境支持,可以使用structuredCloneAPI进行深拷贝:

for (const record of records) {
    s.print("record: " + record);
    this.originalResults.push(structuredClone(record));
    this.results.push(structuredClone(record));
}

内容的提问来源于stack exchange,提问作者user17281101

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 14:02:04