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.