简体   繁体   中英

How does the array property work in JS object

For the following code, why the propB of myObj is updated? And why test.childObj doesn't have the own property propB ?

 var myObj = { propA: '', propB: [] } var fatherObj = { childObj: null, init: function() { this.childObj = Object.create(myObj); this.childObj.propA = 'A'; this.childObj.propB.push(2); } } var test = Object.create(fatherObj); test.init(); console.log(myObj.propB.length); console.log(test.childObj.hasOwnProperty('propA')); console.log(test.childObj.hasOwnProperty('propB')); 

Using Object.create you do not copy an object, but you create a new object that inherits the passed one:

  this.childObj { } ->  myObj { propA: "", propB: [] }

Now when you get a property, it gets looked up in the inheritance chain. As it can't be found in the child object, it gets looked up in myObj . That is usually not a problem, as setting an objects property directly sets it on the object itself without using the inheritance chain, therefore this:

  this.childObj.propA += "test";

looks up propA on myObj but sets propA on the childObj . With reference types however, you do not rewrite the property, therefore this:

  this.childObj.propB.push(1);

looks up propB in myObj , and pushes to that, the result is:

 this.childObj { propA: "test" } ->  myObj { propA: "", propB: [1] }

To resolve that, the childObj has to have its own propB array:

 this.childObj.propB = this.childObj.propB.slice();

That results in:

 this.childObj { propA: "test", propB: [1] } ->  myObj { propA: "", propB: [1] }

and now pushing to propB pushes to the array in childObj .

That is because myObj becomes a prototype of newly created test object (caused by creating it via Object.create .

Due to you just modify underlying prop propB of childObj (and not assign the new value like for propA ) you only modify the prop of prototype. Please read more about inheritance in javascript.

By using Object.create(myObj); , myObj becomes the prototype of childObj . If a property is not found in an object and is found in the prototype, the one from the prototype is used. Also note that hasOwnProperty tells if the objects owns the property itself, and will return false if it exists only in the prototype and not in the object. By assigning directly a property on an object, you set it on the object, not on the prototype, but when you modify the property propB with push, the property is not found directly in childObject, so you modify the prototype.

You have to be careful with that, as all objects created this way will share the same prototype object and by modifying one, you will modify it for all instances.

You have also to be extra careful because it can be tricky to know in javascript where in the prototype chain your property come from, as myObj also have a prototype.

 var myObj = { propA: '', propB: [] } var fatherObj = { childObj: null, init: function() { this.childObj = Object.create(myObj); this.childObj.propA = 'A'; this.childObj.propB.push(2); } } var test = Object.create(fatherObj); test.init(); console.log('test: ', test); console.log('test prototype: ', test.__proto__); console.log('test.childObj: ', test.childObj); console.log('test.childObj prototype: ', test.childObj.__proto__); console.log(test.childObj.hasOwnProperty('propA')); console.log(test.childObj.hasOwnProperty('propB')); 

Javascript inheritance does not work like in most other languages. When using var x = Object.create(someObj) , the new object x is actually empty (it has no properties of its own) along with a reference to its prototype: the separate object someObj .

Any property or function that you then try to access from x which does not resolve there, will be looked up in someObj - or even higher up, if someObj also has a prototype object, etc.

Things can get confusing when inherited properties are modifiable objects , as in your example. As we have seen, trying to modify the array x.propB by pushing an item to it, will actually modify the inherited array contained in someObj .

I'm strongly convinced that it is bad style to have modifiable objects (both arrays and regular objects) in prototype objects. Instead, prototype objects should contain only functions and no data , or at least no data beyond simple strings, numbers and booleans which are not modifiable.

To summarize:

  • Functions are useful to inherit, they are not modifiable (you can't change the function body), and can still be overridden/replaced if needed.
  • Data becomes shared data by all inheritors, unless/until they override it by a re-assignment, which is a burden in itself.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM