Home > Software engineering >  JSON.parse(str) somehow retrieved very specific information of __proto__ of original object, which i
JSON.parse(str) somehow retrieved very specific information of __proto__ of original object, which i

Time:12-28

In JavaScript, JSON.parse(str); converts a JSON string into a JavaScript Object. What I found peculiar was, even though the JSON string does not contain any information on __proto__ of the Object, JSON.parse(str) sometimes manages to restore the nested properties of __proto__ property.

Example (ran directly in Brave browser's developer tools console):

{

    let user1 = {
      name: "John",
      surname: "Smith",
      isAdmin: false,
      "birthday": new Date(2000, 2, 3),
      "friends": [0,1,2,3]
    };

    user1.__proto__.age = 30;

    const str1 = JSON.stringify(user1, null, 2);

    user1.__proto__ = null;
    user1 = null;
    const user2 = JSON.parse(str1);

    console.log(str1);
    console.log(user2);

    const str2 = `{
      "name": "John",
      "surname": "Smith",
      "isAdmin": false,
      "birthday": "2000-03-02T15:00:00.000Z",
      "friends": [
        0,
        1,
        2,
        3
      ]
    }`;

    const user3 = JSON.parse(str2);

    console.log(user3);

}

Here, user1 is created with such and such properties. And then on its proto I assign age : 30 property. From user1 I made its JSON string with JSON.stringify, and saved it in str1. Now I deleted user1 and its __proto__. Just to make sure, I print str1 and it has no information about __proto__ and age:30, as expected. However, when I create user2 from str1 with JSON.parse, the __proto__'s age:30 is revived. How is this possible? Now I even make the exact same JSON string with variable name str2, and create another object with str2. The result is the same. This is crazy.

Output of console.log(str1);

{
  "name": "John",
  "surname": "Smith",
  "isAdmin": false,
  "birthday": "2000-03-02T15:00:00.000Z",
  "friends": [
    0,
    1,
    2,
    3
  ]
}

Output of console.log(user2);

{name: 'John', surname: 'Smith', isAdmin: false, birthday: '2000-03-02T15:00:00.000Z', friends: Array(4)}
birthday: "2000-03-02T15:00:00.000Z"
friends: (4) [0, 1, 2, 3]
isAdmin: false
name: "John"
surname: "Smith"
[[Prototype]]: Object
age: 30
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
__proto__: (...)
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()

Output of console.log(user3);

{name: 'John', surname: 'Smith', isAdmin: false, birthday: '2000-03-02T15:00:00.000Z', friends: Array(4)}
birthday: "2000-03-02T15:00:00.000Z"
friends: (4) [0, 1, 2, 3]
isAdmin: false
name: "John"
surname: "Smith"
[[Prototype]]: Object
age: 30
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
__proto__: (...)
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()

By the way I turned the browser off and on and reran the code block to make sure it wasn't a result of some pre assigned variables. The result was the same and it (age: 30) wasn't coming from pre assigned variables or cached value whatsoever.

Also I opened a fresh tab and executed the following smaller code block.

{
    const str2 = `{
      "name": "John",
      "surname": "Smith",
      "isAdmin": false,
      "birthday": "2000-03-02T15:00:00.000Z",
      "friends": [
        0,
        1,
        2,
        3
      ]
    }`;

    const user3 = JSON.parse(str2);

    console.log(user3);
}

In this case, age: 30 property in the proto is not there, which is the original expected result to have happened with first example, at least for me.

Output with smaller code block:

{name: 'John', surname: 'Smith', isAdmin: false, birthday: '2000-03-02T15:00:00.000Z', friends: Array(4)}
birthday: "2000-03-02T15:00:00.000Z"
friends: (4) [0, 1, 2, 3]
isAdmin: false
name: "John"
surname: "Smith"
[[Prototype]]: Object
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
__proto__: (...)
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()

CodePudding user response:

So it seems that I misused prototype. Correct way to delete age : 30 would be

Either

delete user1.__proto__.age;

Or

delete Object.prototype.age;

And here user1=null is irrelevant.

For some reason user1.__proto__ = null achieves nothing and it would be great if someone hints me on why

Edit: user1.__proto__ = null does remove proto of user1 but it doesn't affect Object.prototype. I think it only detaches user1 from prototype chain.

  • Related