V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
WangLiCha
V2EX  ›  程序员

为什么 console.log 直接打印的对象和打印 JSON.stringify 后的对象会有差异?

  •  1
     
  •   WangLiCha · 350 天前 · 1888 次点击
    这是一个创建于 350 天前的主题,其中的信息可能已经有所发展或是发生改变。

    开发用到了 AntV G6 ,然后发现有一个对象的字段怎么都获取不到,但是直接打印对象又能看到这个字段。具体来说是这样:

    我想通过一个内置的方法来设置边的属性,需要拿到 edge 对象的 targetNode 字段。直接用 console.log 打印 edge 方法是能看到确实有一个 targetNode 字段的,但是实际运行的时候 edge.targetNode 的值一直是 undefined 。接下来我就发现直接打印 edge 输出的结果和打印 JSON.stringify(edge)看到的结果是不一样的,前者会多出好几个字段。

    Snipaste_2023-12-08_14-30-16.png

    Snipaste_2023-12-08_14-30-03.png

    这是个什么原理?为什么会有这种状况出现?这种条件下我怎么才能获取到我想要的 targetNode 字段呢?

    14 条回复    2023-12-08 18:14:25 +08:00
    R1hu6Hs2sSN8pkVX
        1
    R1hu6Hs2sSN8pkVX  
       350 天前
    有没有可能你操作完之后这个 targetNode 被改成 undefined 得了,stringfy 的时候只是字符串不会和原来对象有引用关系
    WangLiCha
        2
    WangLiCha  
    OP
       350 天前
    @whatFoxSay 没有操作过啊,我对 edge 对象的所有操作就是这一行打印了。另外 log 打印出来的对象会被引用影响的话不会干扰开发调试吗?
    flyqie
        3
    flyqie  
       350 天前 via Android
    JSON.stringify 跟 console.log 的结果一定会有差异,JSON.stringify 只会处理标准值,但 js 里面的黑魔法太多,所以你最好还是看下那些字段是怎么加上的。。
    codehz
        4
    codehz  
       350 天前
    祖传问题了,devtools 点开直接打印的对象的时候会当场求值,如果那时候引用被改了就凉了,我记得 chrome 甚至会给一个警告
    flyqie
        5
    flyqie  
       350 天前 via Android
    @flyqie #3

    其实也不能算黑魔法。。叫习惯了。。
    cxe2v
        6
    cxe2v  
       350 天前   ❤️ 1
    直接 console.log 输出是这个对象的引用,内部的字段在 console 的时候不一定有最后你展开时候查看到的值

    JSON.stringify 是把对象字符串化,得到的结果就固定在 stringify 这个时刻

    你这个情况的原因是因为 targetNode 这个字段是在你 console 之后才被添加到 edge 对象里的

    解决方法,尝试通过 nextTick 或者 setTimeout 延迟获取 targetNode
    liuhuihao
        7
    liuhuihao  
       350 天前
    原因是 devtools console.log 的时候有些时候是你点击展开的时候才去获取这个值而不是打印的时候的值。
    至于 console.log 和 JSON.stringify 不一样的原因那就他太多了,toJSON 方法、undefined 、非可枚举一类的都不会出现在 JSON.stringify 里,但你发的这个问题应该不是这个导致的。单纯是你打印的时候本身就是 undefined ,然后在你展开属性的时候获取的是改后的值
    liuhuihao
        8
    liuhuihao  
       350 天前   ❤️ 1
    @liuhuihao

    const a = {a:{}}
    console.log(a,JSON.stringify(a))
    a.targetNode=123

    简单的代码可以试一下
    console.log 打印的是对象的引用,你后给这个对象加上属性也会出现在 log 里
    liuhuihao
        9
    liuhuihao  
       350 天前
    @WangLiCha 你 打印 的时候这个对象里其实还没有 targetNode 字段,这也就是为啥 JSON.stringify 里没有,这个 targetNode 字段是在你 console.log 代码执行之后、点击 devtools 对象的展开之前 才被添加到对象上的。
    guorenjie
        10
    guorenjie  
       350 天前
    wangtian2020
        11
    wangtian2020  
       350 天前
    打印的是对象引用,要么加上 debugger 暂停下面的代码,要么完全拷贝一份对象并且保证切断对象间的数据引用
    NerbraskaGuy
        12
    NerbraskaGuy  
       349 天前
    用 proxy 做数据监听吧,json stringfy 会因为各种限制导致数据丢失
    shanhaisilu
        13
    shanhaisilu  
       349 天前
    let obj = {a:1};
    console.log(obj);
    obj.b = 2

    运行一下这三句代码,看看控制台输出的对象,再把对象展开看看,就很明确了。控制台直接输出的时候,输出的引用地址的引用,展开之后能看到的是引用地址内的内容,而这个内容是可以在输出之后被修改的
    minglanyu
        14
    minglanyu  
       349 天前
    console.log 的时候 targetNode 还没有被添加到 edge 上。
    因为 JOSN.stringify()打印的是对象的快照而不是引用,所以 JOSN.stringify()时没有打印出结果。
    但是因为 console.log 打印的是引用,所以也会打印出 TargetNode 。

    可以试下用 Proxy 劫持下 edge ,当 targetNode 被添加时,打印对象。

    把下面例子里的 targetObject 换成 edge 试下。

    ```
    const targetObject = {};

    const proxy = new Proxy(targetObject, {
    defineProperty(target, property, descriptor) {
    console.log(`试图向属性 ${property} 添加值`);

    // 允许正常添加名为 "targetNode" 的属性
    const result = Reflect.defineProperty(target, property, descriptor);

    // 打印被劫持对象的值和修改后的对象
    console.log("被劫持对象的值:", targetObject);
    console.log("修改后的对象:", proxy);

    return result;
    }
    });

    // 通过代理添加属性
    proxy.targetNode = "some value"; // 试图向属性 targetNode 添加值
    // 输出: 被劫持对象的值: { targetNode: 'some value' }
    // 输出: 修改后的对象: { targetNode: 'some value' }
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5262 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 09:09 · PVG 17:09 · LAX 01:09 · JFK 04:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.