Reputation: 322
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'));
Upvotes: 11
Views: 754
Reputation: 24157
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:
Upvotes: 1
Reputation: 6151
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'));
Upvotes: 2
Reputation: 138277
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
.
Upvotes: 11
Reputation: 7428
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.
Upvotes: 3