Proposal: add an option to omit prototype of objects created by JSON.parse()

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

Proposal: add an option to omit prototype of objects created by JSON.parse()

段垚
It is usually a bad practice to let a map-like object (an plain object
used as a key-value map) have a prototype.

Objects created by JSON.parse() have a prototype by default, and we can
get rid of them by:


JSON.parse(str, function(k, v) {

      if (v && typeof v === 'object' && !Array.isArray(v)) {

         v.__proto__ = null;

      }

      return v;

});


However, implementors warn that mutating prototype causes "performance
hazards" [1].

How about adding an option to omit prototype of objects created by
JSON.parse()?

E.g.:


JSON.parse(str, { noPrototype: true });


[1]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/The_performance_hazards_of__%5B%5BPrototype%5D%5D_mutation


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: add an option to omit prototype of objects created by JSON.parse()

Danielle McLean
From: 段垚 (mailto:[hidden email])
Date: 28 September 2016 at 16:36:52

> [I]mplementors warn that mutating prototype causes "performance
> hazards".

You don't actually need to mutate any prototypes to get prototypeless
objects out of `JSON.parse` - the reviver function is allowed to
return a new object instead, so you can create a fresh one with the
correct prototype. For example:

```js
JSON.parse(string, function(k, v) {
  if (v && typeof v === 'object' && !Array.isArray(v)) {
    return Object.assign(Object.create(null), v);
  }
  return v;
});
```

> How about adding an option to omit prototype of objects created by
> JSON.parse()?
>
> JSON.parse(str, { noPrototype: true });

While you can achieve the appropriate result already without extra
language support, I think this particular situation is common enough
that such an option might be a good idea.

How would you expect this new parameter to interact with the existing
second parameter to `JSON.parse`, the reviver function? I'd suggest
that the cleanest way to add the options would be for the second
parameter to be either an object or function, like this:

```js
JSON.parse(str, someFunc);
// equivalent to
JSON.parse(str, {reviver: someFunc});
```

Another approach would be to use two separate optional parameters,
i.e., `JSON.parse(text[, reviver][, options])`, but generally that
style is very messy.
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: add an option to omit prototype of objects created by JSON.parse()

段垚
在 2016/9/28 22:59, Danielle McLean 写道:

> From: 段垚 (mailto:[hidden email])
> Date: 28 September 2016 at 16:36:52
>
>> [I]mplementors warn that mutating prototype causes "performance
>> hazards".
> You don't actually need to mutate any prototypes to get prototypeless
> objects out of `JSON.parse` - the reviver function is allowed to
> return a new object instead, so you can create a fresh one with the
> correct prototype. For example:
>
> ```js
> JSON.parse(string, function(k, v) {
>    if (v && typeof v === 'object' && !Array.isArray(v)) {
>      return Object.assign(Object.create(null), v);
>    }
>    return v;
> });
> ```

Yes, but creating a copy also has performance penalty. This proposal is made for performance.

>> How about adding an option to omit prototype of objects created by
>> JSON.parse()?
>>
>> JSON.parse(str, { noPrototype: true });
> While you can achieve the appropriate result already without extra
> language support, I think this particular situation is common enough
> that such an option might be a good idea.
>
> How would you expect this new parameter to interact with the existing
> second parameter to `JSON.parse`, the reviver function? I'd suggest
> that the cleanest way to add the options would be for the second
> parameter to be either an object or function, like this:
>
> ```js
> JSON.parse(str, someFunc);
> // equivalent to
> JSON.parse(str, {reviver: someFunc});
> ```
Looks reasonable.

Maybe we can simply add another method, say
`JSON.parseWithoutPrototype()`. This makes feature detection easier.


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: add an option to omit prototype of objects created by JSON.parse()

Michał Wadas
Have you ever encountered performance issue because of copying object on deserialization?

https://gist.github.com/Ginden/381448a17f50c7669b9a3693742e3a3d For me results are:

Simple JSON.parse, 100000 iterations: 39.594999999997526ms
JSON.parse with copy, 100000 iterations: 184.5699999999997ms
JSON.parse with __proto__ change, 100000 iterations: 89.75500000000102ms




On Wed, Sep 28, 2016 at 5:49 PM, 段垚 <[hidden email]> wrote:
在 2016/9/28 22:59, Danielle McLean 写道:

From: 段垚 (mailto:[hidden email])
Date: 28 September 2016 at 16:36:52

[I]mplementors warn that mutating prototype causes "performance
hazards".
You don't actually need to mutate any prototypes to get prototypeless
objects out of `JSON.parse` - the reviver function is allowed to
return a new object instead, so you can create a fresh one with the
correct prototype. For example:

```js
JSON.parse(string, function(k, v) {
   if (v && typeof v === 'object' && !Array.isArray(v)) {
     return Object.assign(Object.create(null), v);
   }
   return v;
});
```

Yes, but creating a copy also has performance penalty. This proposal is made for performance.

How about adding an option to omit prototype of objects created by
JSON.parse()?

JSON.parse(str, { noPrototype: true });
While you can achieve the appropriate result already without extra
language support, I think this particular situation is common enough
that such an option might be a good idea.

How would you expect this new parameter to interact with the existing
second parameter to `JSON.parse`, the reviver function? I'd suggest
that the cleanest way to add the options would be for the second
parameter to be either an object or function, like this:

```js
JSON.parse(str, someFunc);
// equivalent to
JSON.parse(str, {reviver: someFunc});
```
Looks reasonable.

Maybe we can simply add another method, say `JSON.parseWithoutPrototype()`. This makes feature detection easier.



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: add an option to omit prototype of objects created by JSON.parse()

段垚

在 2016/9/29 0:04, Michał Wadas 写道:

Have you ever encountered performance issue because of copying object on deserialization?
Not yet.

https://gist.github.com/Ginden/381448a17f50c7669b9a3693742e3a3d For me results are:

Simple JSON.parse, 100000 iterations: 39.594999999997526ms
JSON.parse with copy, 100000 iterations: 184.5699999999997ms
JSON.parse with __proto__ change, 100000 iterations: 89.75500000000102ms


Thanks for writing this test. I got similar result on Firefox; while on Chrome and Edge, there is not much difference between coping and changing __proto__ .

However, it is not easy to messure overhead of GC after copy and the effect of __proto__ change on property access performance.


On Wed, Sep 28, 2016 at 5:49 PM, 段垚 <[hidden email]> wrote:
在 2016/9/28 22:59, Danielle McLean 写道:

From: 段垚 (mailto:[hidden email])
Date: 28 September 2016 at 16:36:52

[I]mplementors warn that mutating prototype causes "performance
hazards".
You don't actually need to mutate any prototypes to get prototypeless
objects out of `JSON.parse` - the reviver function is allowed to
return a new object instead, so you can create a fresh one with the
correct prototype. For example:

```js
JSON.parse(string, function(k, v) {
   if (v && typeof v === 'object' && !Array.isArray(v)) {
     return Object.assign(Object.create(null), v);
   }
   return v;
});
```

Yes, but creating a copy also has performance penalty. This proposal is made for performance.

How about adding an option to omit prototype of objects created by
JSON.parse()?

JSON.parse(str, { noPrototype: true });
While you can achieve the appropriate result already without extra
language support, I think this particular situation is common enough
that such an option might be a good idea.

How would you expect this new parameter to interact with the existing
second parameter to `JSON.parse`, the reviver function? I'd suggest
that the cleanest way to add the options would be for the second
parameter to be either an object or function, like this:

```js
JSON.parse(str, someFunc);
// equivalent to
JSON.parse(str, {reviver: someFunc});
```
Looks reasonable.

Maybe we can simply add another method, say `JSON.parseWithoutPrototype()`. This makes feature detection easier.



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: add an option to omit prototype of objects created by JSON.parse()

Olivier Lalonde
Given that JSON.parse doesn't necessarily return an object, would the noPrototype option would be ignored on e.g. `JSON.parse('"some string"')` or `JSON.parse('true')`?

On Wed, Sep 28, 2016 at 9:46 AM, 段垚 <[hidden email]> wrote:

在 2016/9/29 0:04, Michał Wadas 写道:

Have you ever encountered performance issue because of copying object on deserialization?
Not yet.

https://gist.github.com/Ginden/381448a17f50c7669b9a3693742e3a3d For me results are:

Simple JSON.parse, 100000 iterations: 39.594999999997526ms
JSON.parse with copy, 100000 iterations: 184.5699999999997ms
JSON.parse with __proto__ change, 100000 iterations: 89.75500000000102ms


Thanks for writing this test. I got similar result on Firefox; while on Chrome and Edge, there is not much difference between coping and changing __proto__ .

However, it is not easy to messure overhead of GC after copy and the effect of __proto__ change on property access performance.



On Wed, Sep 28, 2016 at 5:49 PM, 段垚 <[hidden email]> wrote:
在 2016/9/28 22:59, Danielle McLean 写道:

From: 段垚 (mailto:[hidden email])
Date: 28 September 2016 at 16:36:52

[I]mplementors warn that mutating prototype causes "performance
hazards".
You don't actually need to mutate any prototypes to get prototypeless
objects out of `JSON.parse` - the reviver function is allowed to
return a new object instead, so you can create a fresh one with the
correct prototype. For example:

```js
JSON.parse(string, function(k, v) {
   if (v && typeof v === 'object' && !Array.isArray(v)) {
     return Object.assign(Object.create(null), v);
   }
   return v;
});
```

Yes, but creating a copy also has performance penalty. This proposal is made for performance.

How about adding an option to omit prototype of objects created by
JSON.parse()?

JSON.parse(str, { noPrototype: true });
While you can achieve the appropriate result already without extra
language support, I think this particular situation is common enough
that such an option might be a good idea.

How would you expect this new parameter to interact with the existing
second parameter to `JSON.parse`, the reviver function? I'd suggest
that the cleanest way to add the options would be for the second
parameter to be either an object or function, like this:

```js
JSON.parse(str, someFunc);
// equivalent to
JSON.parse(str, {reviver: someFunc});
```
Looks reasonable.

Maybe we can simply add another method, say `JSON.parseWithoutPrototype()`. This makes feature detection easier.



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss




--
- Oli

Olivier Lalonde
http://www.syskall.com <-- connect with me!

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: add an option to omit prototype of objects created by JSON.parse()

Danielle McLean
From: Olivier Lalonde (mailto:[hidden email])
Date: 30 September 2016 at 07:21:10

> Given that JSON.parse doesn't necessarily return an object, would the noPrototype option would be ignored on e.g. `JSON.parse('"some string"')` or `JSON.parse('true')`?

The noPrototype option should set the behaviour whenever the parser
encounters a JSON object (i.e., key-value pairs wrapped in curly
braces). If the JSON text to be parsed does not contain any objects,
then the option will have no effect. 'true', 'some string', and
'[1,2,3,"four"]' are examples of JSON texts which would not be
affected by the noPrototype option. (Note that although an array is a
*JavaScript* object, it's not a *JSON* object.)

Conversely, if the JSON text to be parsed contains multiple objects -
for instance, `JSON.parse('[{"first": 1}, {"second": 2}, {"third":
3}]', {noPrototype: true})` - then *all* of those objects should be
constructed with a null prototype.
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: add an option to omit prototype of objects created by JSON.parse()

Rick Waldron
var o = JSON.parse('{}');
Object.setPrototypeOf(o, null);


Rick

On Thu, Sep 29, 2016 at 9:30 PM Danielle McLean <[hidden email]> wrote:
From: Olivier Lalonde (mailto:[hidden email])
Date: 30 September 2016 at 07:21:10

> Given that JSON.parse doesn't necessarily return an object, would the noPrototype option would be ignored on e.g. `JSON.parse('"some string"')` or `JSON.parse('true')`?

The noPrototype option should set the behaviour whenever the parser
encounters a JSON object (i.e., key-value pairs wrapped in curly
braces). If the JSON text to be parsed does not contain any objects,
then the option will have no effect. 'true', 'some string', and
'[1,2,3,"four"]' are examples of JSON texts which would not be
affected by the noPrototype option. (Note that although an array is a
*JavaScript* object, it's not a *JSON* object.)

Conversely, if the JSON text to be parsed contains multiple objects -
for instance, `JSON.parse('[{"first": 1}, {"second": 2}, {"third":
3}]', {noPrototype: true})` - then *all* of those objects should be
constructed with a null prototype.
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: add an option to omit prototype of objects created by JSON.parse()

Tab Atkins Jr.
On Thu, Oct 6, 2016 at 12:50 PM, Rick Waldron <[hidden email]> wrote:
> var o = JSON.parse('{}');
> Object.setPrototypeOf(o, null);

That's not remotely correct, as it does nothing for anything other
than the top object.  (And it breaks things if the top-level value
isn't an object.)

~TJ
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss