简体   繁体   中英

Weird behaviour with Object.create on array properties

When creating a new object using Object.create() with a prototype object, it seems that the new object keeps a REFERENCE to the prototype for array properties.

Example Code

var obj = { color: ['white'], cat: 'Kitty', state: {}};
obj2 = Object.create(obj);
obj2.color.push('blue');
obj2.color.push('red');
obj2.color.push('yellow');
obj2.cat = 'Fluffy';
obj2.state = {complete: false};
console.log('obj2 color = ' + JSON.stringify(obj2.color) + ', obj2.cat = ' + obj2.cat + ', state = ' + JSON.stringify(obj2.state));
console.log('obj color = ' + JSON.stringify(obj.color) + ', obj.cat = ' + obj.cat + ', state = ' + JSON.stringify(obj.state));

Result

obj2 color = ["white","blue","red","yellow"], obj2.cat = Fluffy, state = {"complete":false}
obj color = ["white","blue","red","yellow"], obj.cat = Kitty, state = {}

The string property 'cat' in the new obj2 has the expected behaviour and is independent from that property in the prototype object 'obj'. Same with the object property 'state'.

However, on the array 'color', when I change the array, it also changes on the prototype object!

Is that intended in Javascript? For me, coming from an object-oriented background, this is totally unexpected. I see no logic in that. What is different about an array?

I could even see some logic if value-types like strings would behave different than an object property but they do not (as this example shows) - yet arrays behave differently.

Assignment to an object property:

obj.xyz = "hello world";

always updates (creating if necessary) the property directly on the target object.

Pushing values into an array, however, does not constitute "assignment to an object property". Note that, given your code,

obj2.color = ["green"];

will create a new "color" property directly on the target object.

In the statement

obj2.color.push('blue');

the "color" property is found in the look-up operation on the prototype object. Then, via that object reference (the reference to the "color" property on the prototype), the "push" property is looked up. That's found eventually on the Array.prototype object. Then that value is called as a function. Nothing in that process involves updating a property value on "obj2".

That the "color" property on the prototype is an array is not particularly special. You'll see similar effects for any object reference. Consider:

var proto = { obj: { a: 1, b: 2 } };
var obj2 = Object.create(proto);
obj2.obj.a = 3;

That will change the "obj" object in the prototype.

It should be noted finally that usually the prototype is the source of function references, not simple values.

TLDR: setting a property of an object sets it directly on that object. Getting a property goes up the prototype chain.

 obj2.color

That gets the array of the prototype.

In addition to Pointy's well explained answer , you can do something like this to create a deep cloned object.

Note though, as commented by Pointy, this might not work on "all" objects.

var new_obj = JSON.parse(JSON.stringify(old_obj));`

Stack snippet

 var obj = { color: ['white'], cat: 'Kitty', state: {}}; var obj2 = JSON.parse(JSON.stringify(obj)); obj2.color.push('blue'); obj2.color.push('red'); obj2.color.push('yellow'); obj2.cat = 'Fluffy'; obj2.state = {complete: false}; console.log('obj2 color = ' + JSON.stringify(obj2.color) + ', obj2.cat = ' + obj2.cat + ', state = ' + JSON.stringify(obj2.state)); console.log('obj color = ' + JSON.stringify(obj.color) + ', obj.cat = ' + obj.cat + ', state = ' + JSON.stringify(obj.state)); 

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