JSON中的JSON.stringify()是否具有确定性?

bri*_*out 10 javascript v8 memoization node.js

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

不能保证它在规范级别上具有确定性.

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

编辑:

确定性我的意思是,无论价值json_str是什么,跟随断言都是正确的.(给定值是有效的JSON字符串.)

const obj = JSON.parse(json_str);
assert(JSON.stringify(obj)===JSON.stringify(obj)); // always true
Run Code Online (Sandbox Code Playgroud)

编辑2:

实际上,我也对下面的断言感兴趣

if( deepEqual(obj1, obj2) ) {
    assert(JSON.stringify(obj1)===JSON.stringify(obj2))
}
Run Code Online (Sandbox Code Playgroud)

事实并非如此(见答案).

bri*_*out 12

澄清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}');
Run Code Online (Sandbox Code Playgroud)

因此,保证断言是正确的

if( obj1 === obj2 ) {
  assert(JSON.stringify(obj1) === JSON.stringify(obj2));
}
Run Code Online (Sandbox Code Playgroud)

但是两个"深度相等"的对象可能被序列化为不同的字符串;

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}');
Run Code Online (Sandbox Code Playgroud)

规格报价;

  1. 让密钥成为新的空列表.
  2. 对于每个自己的属性键P,O是一个整数索引,按升序数字索引顺序,执行

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

  3. 对于每个属性的键P,它是一个String但不是整数索引,按照属性创建的升序顺序排列,

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

  4. 对于作为符号的O的每个自有属性密钥P,按照属性创建的递增时间顺序,执行

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

  5. 返回键.

来自https://tc39.github.io/ecma262/#sec-ordinaryownpropertykeys


jmr*_*mrk 6

如果"确定性"你的意思是对象的属性枚举顺序:这实际确定,并遵循V8规范.请参阅https://tc39.github.io/ecma262/#sec-ordinaryownpropertykeys.[编辑:这是你澄清定义的答案,所以是的,JSON.stringify在这个意义上是确定性的.]

如果用"确定性"表示"总是为同一个输入对象返回相同的字符串",那么,好吧,没有:-)

> var o = { toJSON: function() { return Math.random(); } }
> JSON.stringify(o);
< "0.37377773963616434"
> JSON.stringify(o);
< "0.8877065604993732"
Run Code Online (Sandbox Code Playgroud)

Proxy对象和replacer参数JSON.stringify也可用于创建任意行为(即使它JSON.stringify本身总是做同样的事情).

如果"确定性"是指其他内容,请注明.

  • 遵循链:JSON.stringify -&gt; SerializeJSONProperty -&gt; SerializeJSONObject -&gt; EnumerableOwnProperties -&gt; [[OwnPropertyKeys]] -&gt; OrdinaryOwnPropertyKeys。您明确的定义正是如此。 (2认同)

fod*_*ma1 5

万一有人会寻找一个可以预测 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) }),
    ))
);
Run Code Online (Sandbox Code Playgroud)

这是一个组合的确定性 JSON 转储:

const deterministicStrigify = obj => JSON.stringify(deterministic(sortObj))
Run Code Online (Sandbox Code Playgroud)

它适用于上面的示例:

> 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}'
Run Code Online (Sandbox Code Playgroud)