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)
规格报价;
- 让密钥成为新的空列表.
对于每个自己的属性键P,O是一个整数索引,按升序数字索引顺序,执行
一个.添加P作为键的最后一个元素.
对于每个属性的键P,它是一个String但不是整数索引,按照属性创建的升序顺序排列,
一个.添加P作为键的最后一个元素.
对于作为符号的O的每个自有属性密钥P,按照属性创建的递增时间顺序,执行
一个.添加P作为键的最后一个元素.
- 返回键.
来自https://tc39.github.io/ecma262/#sec-ordinaryownpropertykeys
如果"确定性"你的意思是对象的属性枚举顺序:这是实际确定,并遵循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 转储的函数,我写了一个:
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)