fast-memoize.js icon indicating copy to clipboard operation
fast-memoize.js copied to clipboard

JSON.stringify doesn't preserve object equivalence

Open chocolateboy opened this issue 8 years ago • 10 comments

https://gist.github.com/chocolateboy/254b44382e6c34f8ce7e

chocolateboy avatar Jan 26 '17 19:01 chocolateboy

This cannot happen if your args are only primitive values.

memoized('foo', 3, 'bar')

Your order is guaranteed when you contruct the object manually.

c = "bar"
a = { foo: c  }
b = 20
memoized({ key1: a, key2: b })

but yes if you pass a random generated object the order is not guaranteed.

StarpTech avatar Jan 29 '17 12:01 StarpTech

@StarpTech any source on "order being guaranteed" for Objects? AFAIK they may explicitly be random.

FlorianWendelborn avatar Jan 29 '17 17:01 FlorianWendelborn

@dodekeract ES6 defines the order in which the keys of an object are enumerated: http://www.2ality.com/2015/10/property-traversal-order-es6.html

NeoPhi avatar Feb 05 '17 16:02 NeoPhi

@NeoPhi

For example, JSON.stringify(obj) will always produce the same result, as long as obj is created in the same manner.

I'd love to see a source for that statement (@rauschma?). The spec itself says otherwise:

An object is an unordered collection of zero or more name/value pairs

Either way, it doesn't resolve this issue:

$ node --version
v7.5.0
$ node
> JSON.stringify({ foo: "bar", baz: "quux" })
'{"foo":"bar","baz":"quux"}'
> JSON.stringify({ baz: "quux", foo: "bar" })
'{"baz":"quux","foo":"bar"}'

chocolateboy avatar Feb 05 '17 17:02 chocolateboy

The problem is that the traversal order discussed in that article is not for all object traversals period. There is no universal order expectation for how objects will display, however a majority of engines today will show them in the order in which they were added. This is not guaranteed by any means, but pretty much all mainstream browsers follow that paradigm for simplicity and performance reasons.

More details => http://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order/38218582#38218582

planttheidea avatar Feb 05 '17 17:02 planttheidea

@chocolateboy ES6 spec defines JSON.stringify (independent of the RFC) to use the same enumeration sequence, making the order guaranteed: http://www.ecma-international.org/ecma-262/6.0/#sec-serializejsonobject

You'd need to use a custom serializer to make { foo: "bar", baz: "quux" } equal { baz: "quux", foo: "bar" } if JSON.stringify is used.

NeoPhi avatar Feb 05 '17 17:02 NeoPhi

@planttheidea That Stack Overflow link is talking about ES2015. ES2016 clarified the ordering for all cases to be the same.

NeoPhi avatar Feb 05 '17 17:02 NeoPhi

Add how many browsers have actually implemented the spec? There is a practicality piece in here, as implementing a custom serializer instead of leveraging JSON.stringify as the default will cause a fairly substantial performance decrease.

Naturally you can do it for your specific application, but this to call the native browser implementation an issue may be too much.

planttheidea avatar Feb 05 '17 17:02 planttheidea

@NeoPhi Interesting. Thanks for the link!

You'd need to use a custom serializer to make { foo: "bar", baz: "quux" } equal { baz: "quux", foo: "bar" } if JSON.stringify is used.

Well, yes. While this excursion is interesting, it doesn't address this issue (other than confirming it).

chocolateboy avatar Feb 05 '17 17:02 chocolateboy

https://www.npmjs.com/package/canonicalize this fixes this problem.

cauefcr avatar Dec 06 '20 18:12 cauefcr