What happens can be easily illustrated with a few examples:
Accessing personal_details files through the prototype chain
var person = { name :"dummy", personal_details: { age : 22, country : "USA" } } var bob = Object.create(person) bob.name = "bob" bob.personal_details.age = 23
What outputs:
console.log( bob ); /// { name :"bob", personal_details: { age : 23, country : "USA" } } console.log( person ) /// { name :"dummy", personal_details: { age : 23, country : "USA" } }
Age 23 is now set on the person object because bob.personal_details is a direct reference to person.personal_details through the bob prototype chain. When you navigate the structure of an object, you work directly with the person.personal_details object.
Overriding a prototyped property with a local property
However, if you override the bob personal_details property with another object, this prototype link will be overridden by a more local property.
bob.personal_details = { a: 123 }
Now the conclusion:
console.log( bob ); /// { name :"bob", personal_details: { a : 123 } } console.log( person ) /// { name :"dummy", personal_details: { age : 23, country : "USA" } }
So, turning to bob.personal_details - from now on you refer to the object { a: 123 } not the original { age : 23, country : "USA" } person’s object. All changes made will occur on this object and basically have nothing to do with the bob or person object.
Prototype chain
To keep things interesting, what do you think happens when you do this, after all of the above:
delete bob.personal_details
As a result, you restore the link of the original prototype to person.personal_details (because you deleted the added local property), so the console log will show:
console.log( bob ); /// { name :"bob", personal_details: { age : 23, country : "USA" } }
Basically, the JavaScript engine will work through the prototype chain until it finds the property or method that you request on each prototype object. A further chain defined by an element will mean that it will later redefine the rest.
Now another question: what happens if you run the following again:
delete bob.personal_details
Nothing, there is no longer the actual property assigned by bob called personal_details , delete will only work with the current object and will not follow the prototype chain.
Another way to look at it
Another way to look at how the prototype chain works is basically a stack of objects. When JavaScript scans a specific property or method, it will read down the following structure:
bob : { } person : { name: 'dummy', personal_details: { age: 22 } } Object : { toString: function(){ return '[Object object]'; } }
So for example, I would like to access bob.toString . toString is a method that exists on the base JavaScript Object , which is the base prototype for almost everything. When the interpreter receives a read request for a specific method or property for an object, it will follow this chain of events:
- Does
bob property called toString ? No. - Does
bob.__proto__ ie person property called toString ? No. - Does
bob.__proto__.__proto__ ie Object property called toString ? Yes - Return the link to
function(){ return '[Object object]'; } function(){ return '[Object object]'; }
Once it reaches point 4, the interpreter will return a reference to the toString method found in Object. If the property was not found on Object , the undefined property error was most likely fired (since it is the last in the chain).
Now, if we take an example earlier and this time define the toString method on bob - like this:
bob : { toString: function(){ return '[Bob]'; } } person : { name: 'dummy', personal_details: { age: 22 } } Object : { toString: function(){ return '[Object object]'; } }
If we try to read the toString method on bob again, this time we get:
- Does
bob property called toString ? Yes.
The process stops at the first obstacle and returns the toString method from bob. This means that bob.toString() will return [Bob] , not [Object object] .
As Phant0m has been concisely stated, write requests for an object follow a different path and will never navigate the prototype chain. Understanding this is to determine the difference between what you read and what constitutes a write request.
bob.toString
The last element is one that causes confusion. And this process will follow this route:
- Does
bob property called personal_details ? No. - Does
person property called personal_details ? Yes. - Return the link to
{ age: 22 } , which is stored somewhere in memory.
Now a new process begins, because each part of the navigation or assignment of an object is a new request for a property or method. So now we have our personal_details object, which we go to the write request, because the property or variable on the left has the side = equals is always an assignment.
- Enter
123 into the age property of { age: 22 }
Thus, the initial request could be seen as something like this:
(bob.personal_details) --- read (personal_details.age = 123) --- write
If bob owned its own personal_details property, the process would be the same, but the target that was written would be different.
And finally ...
Share the lines of your question:
It is difficult to digest that the properties of prototype objects are considered as READ_ONLY, but if the property is an object, then you can master it and freely change its properties! Do I understand correctly?
Prototyped properties seem to be read-only, but only when directly accessing them as an object that inherits them, because these properties do not exist at all on the inheriting object. If you go to the prototype object itself, it can be processed in the same way as any normal object (with reading and writing), because this is exactly what it is - a normal object. This may be confusing at first, but it is the inheritance of nature or prototype, all about how you access the properties that you work with.