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

为何array.map会将类实例转为普通对象?求解决方法

问题描述

我有一个存储User类实例的Users[]数组,通过映射查询结果创建该数组,代码如下:

function GetUserByUserData(data : UserData[] | ReadonlyArray<UserData>) : User[]
{
    return data.map((userData) => {
        return new User(userData);
    });
}

注:UserData是类型为{id: number; [key: string]: any;}的对象。

随后我通过方法基于类属性创建行,相关代码如下:

//User类
export default class User
{
    private _data: UserData;
    get data()
    {
        return this._data;
    }
}
let rows = Users.map((user) => makeRowFromUser(user));

function makeRowFromUser(user: User)
{
    console.log(user); //输出 {_data}
    console.log(user.data); //输出 undefined
    console.log(user._data); //正常输出数据
}

我意识到私有属性_data本不应被直接访问,但实际可以;同时user.data返回undefined,console.log(user)显示其为{_data}格式的普通对象。

请问为何会出现这种情况?array.map是否解构了Users[]数组?user参数是否在传入函数前被解构?该如何避免此问题,确保传递的是User类实例?

补充:实际代码流程如下:

  1. 调用API获取用户;
  2. 查询数据库得到unknown[],并转换为UserData[],代码如下:
function ConvertUknownResultToUserData(raw : unknown[]) : ReadonlyArray<UserData>
{
    return raw.map<UserData>((e : any) : UserData => ({
        id: e.user_id,
        name_first: e.name_first,
        name_last: e.name_last,
        //...
    }));
}
  1. 通过GetUserByUserData方法创建User[];
  2. 在Svelte组件中,Promise解析后映射User[]生成行:
//Svelte组件内部...
await window.api.GetUsers().
    then((value) => {
        return value.map((user) => makeRowFromUser(user));
    });
  1. 显示表格行。
    问题出现在第4步,即API返回User[]之后。

问题分析与解决

核心原因

你的User实例在API传递过程中被序列化/反序列化成了普通JSON对象,而非类实例。类实例无法直接通过跨进程/API通信传递,序列化时会被转换成仅包含自身可枚举属性的普通对象(也就是你看到的{_data}):

  • user.data返回undefined:类的getter属于原型链方法,序列化后的普通对象没有继承User类的原型,自然访问不到这个getter属性。
  • 能直接访问_data:TypeScript的private只是编译时检查,编译后的JavaScript中_data就是普通对象属性,没有真正的私有性限制。
  • array.map本身不会解构实例,问题出在API返回环节——window.api.GetUsers()返回的不是真正的User实例数组,而是序列化后再反序列化得到的普通对象数组。

解决方法

1. 补充User类的构造函数

你之前的User类缺少构造函数,导致new User(userData)时_data属性根本不会被赋值,这是潜在的关键问题,必须补上:

export default class User
{
    private _data: UserData;
    
    // 新增构造函数
    constructor(data: UserData) {
        this._data = data;
    }

    get data()
    {
        return this._data;
    }
}

2. 在API接收端重新构建User实例

在Svelte组件中,拿到API返回的普通对象后,重新用User类包装:

//Svelte组件内部修改
await window.api.GetUsers().then((rawUsers) => {
    // 将普通对象转为User实例
    const userInstances = rawUsers.map(userObj => new User(userObj._data));
    return userInstances.map(user => makeRowFromUser(user));
});

3. (可选)给User类添加静态反序列化方法

可以封装一个静态方法,更优雅地从普通对象恢复实例:

export default class User
{
    private _data: UserData;
    
    constructor(data: UserData) {
        this._data = data;
    }

    get data()
    {
        return this._data;
    }

    // 静态方法:从普通对象恢复实例
    static fromObject(obj: {_data: UserData}): User {
        return new User(obj._data);
    }
}

使用时:

await window.api.GetUsers().then((rawUsers) => {
    const userInstances = rawUsers.map(User.fromObject);
    return userInstances.map(user => makeRowFromUser(user));
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 23:47:12