Javascript JSON.stringify()在V8中是确定性的吗?

Javascript JSON.stringify()在V8中是确定性的吗?,javascript,node.js,v8,memoization,Javascript,Node.js,V8,Memoization,我还没有看到(现在?JSON.stringify在Node.JS中是不确定的 不能保证它在规范级别上是确定性的 但是V8呢; 它的实现是否具有确定性? 是否保证它在未来的V8版本中保持确定性 编辑: 对于deterministic,我的意思是无论json\u str的值是什么,下面的断言都是正确的。(给定的值是有效的JSON字符串。) 编辑2: 实际上,我也希望下面的断言是正确的 if( deepEqual(obj1, obj2) ) { assert(JSON.stringify(ob

我还没有看到(现在?
JSON.stringify
在Node.JS中是不确定的

不能保证它在规范级别上是确定性的

但是V8呢; 它的实现是否具有确定性? 是否保证它在未来的V8版本中保持确定性

编辑:

对于deterministic,我的意思是无论
json\u str
的值是什么,下面的断言都是正确的。(给定的值是有效的JSON字符串。)

编辑2:

实际上,我也希望下面的断言是正确的

if( deepEqual(obj1, obj2) ) {
    assert(JSON.stringify(obj1)===JSON.stringify(obj2))
}
if( obj1 === obj2 ) {
  assert(JSON.stringify(obj1) === JSON.stringify(obj2));
}
但事实并非如此(请参见答案)。

如果“确定性”指的是对象属性的枚举顺序:这实际上是指定的,V8遵循规范。请参见。 [编辑:这是对您明确定义的回答,因此是的,JSON.stringify在这个意义上是确定性的。]

如果“确定性”的意思是“总是为相同的输入对象返回相同的字符串”,那么,好吧,否:-)

>var o={toJSON:function(){return Math.random();}
>JSON.stringify(o);
< "0.37377773963616434"
>JSON.stringify(o);
< "0.8877065604993732"
Proxy
对象和
JSON.stringify
replacer
参数也可用于创建任意行为(即使
JSON.stringify
本身总是做同样的事情)


如果你所说的“确定性”是指其他东西,请具体说明。

用你的术语来说,确定性可以归结为:

  • Order=>对象数据是否按相同的顺序封送
  • 是的,通过对象数据的遍历始终发生在同一个“路由”中

  • Content=>对象数据是否使用相同的内容封送
  • 是的,除非通过toJSON覆盖引入任意性,如上面所解释的@jmrk

  • 并发=>检查之间是否会修改对象数据
  • 不,V8脚本运行程序是单线程的,因此不会出现混乱的访问

  • 规范=>规范中是否有违反确定性的条款
  • 不,除了上下文替换/重写之外,解析器和stringify每次都应该生成相同的数据

  • 兼容性=>所有stringify方法都会生成兼容的数据吗 不,规范没有明确列出对象字段的顺序,因此实现可以自由地迭代对象,这意味着数据可能与“目的”和“精神”相同,而不是逐字节比较


    希望这有帮助

    澄清jmrk的答案

    根据规范,整数键按数字顺序序列化,非整数键按创建属性的时间顺序序列化,例如:

    var o = {};
    o[2] = 2;
    o.a = 3;
    o.b = 4;
    o["1"] = 1;
    
    assert(JSON.stringify(o)==='{"1":1,"2":2,"a":3,"b":4}');
    
    因此,以下断言保证为真

    if( deepEqual(obj1, obj2) ) {
        assert(JSON.stringify(obj1)===JSON.stringify(obj2))
    }
    
    if( obj1 === obj2 ) {
      assert(JSON.stringify(obj1) === JSON.stringify(obj2));
    }
    
    但两个“深度相等”对象可以序列化为不同的字符串

    var obj1 = {};
    obj1["a"] = true;
    obj1["b"] = true;
    assert(JSON.stringify(obj1)==='{"a":true,"b":true}');
    
    var obj2 = {};
    obj2["b"] = true;
    obj2["a"] = true;
    assert(JSON.stringify(obj2)==='{"b":true,"a":true}');
    
    规格报价

  • 让键成为一个新的空列表
  • 对于作为整数索引的O的每个自己的属性键p,按升序数字索引顺序执行

    a。添加P作为键的最后一个元素

  • 对于作为字符串但不是整数索引的O的每个自己的属性键p,按照属性创建的时间升序,执行以下操作

    a。添加P作为键的最后一个元素

  • 对于作为符号的O的每个自己的属性键p,按照属性创建的时间升序,执行以下操作

    a。添加P作为键的最后一个元素

  • 归还钥匙

  • 中,为了防止任何人寻找一个可以使JSON转储可预测的函数,我写了一个:

    const sortObj = (obj) => (
      obj === null || typeof obj !== 'object'
      ? obj
      : Array.isArray(obj)
      ? obj.map(sortObj)
      : Object.assign({}, 
          ...Object.entries(obj)
            .sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
            .map(([k, v]) => ({ [k]: sortObj(v) }),
        ))
    );
    
    下面是一个组合的确定性JSON转储:

    const deterministicStrigify = obj => JSON.stringify(deterministic(sortObj))
    
    它与上面的示例配合得很好:

    > obj1 = {};
    > obj1.b = 5;
    > obj1.a = 15;
    
    > obj2 = {};
    > obj2.a = 15;
    > obj2.b = 5;
    
    > deterministicStrigify(obj1)
    '{"a":15,"b":5}'
    > deterministicStrigify(obj2)
    '{"a":15,"b":5}'
    > JSON.stringify(obj1)
    '{"b":5,"a":15}'
    > JSON.stringify(obj2)
    '{"a":15,"b":5}'
    

    @fodma1的方法可以更简单地使用JSON.stringify的第二个参数实现,即“replacer”函数:

    const deterministicReplacer = (_, v) =>
      typeof v !== 'object' || v === null || Array.isArray(v) ? v :
        Object.fromEntries(Object.entries(v).sort(([ka], [kb]) => 
          ka < kb ? -1 : ka > kb ? 1 : 0));
    
    两者都给出:

    {"a":0,"b":1,"c":{"d":null,"e":[],"f":1}}
    

    出于速度的原因,我还切换到了天真的字符串比较,假设我们只关心排序顺序的可重复性。

    确切地说,确定性是什么意思?不清楚你在问什么。感谢您的澄清。请参阅编辑,谢谢您的评论。OrdinaryOwnPropertyKeys/您的链接与JSON.stringify的关系如何?实际上,我没有看到在中指定任何枚举顺序。至于我对deterministic的意思,请参见OP edit。遵循以下链:JSON.stringify->SerializeJSONProperty->SerializeJSONObject->EnumerableOwnProperties->[[OwnPropertyKeys]]->OrdinaryOwnPropertyKeys。你明确的定义就是这样。
    {"a":0,"b":1,"c":{"d":null,"e":[],"f":1}}