I know this question has been discussed so many times, and I think I got a basic idea. There are some top rated answers I found from StackOverflow:
- What is the difference between a deep copy and a shallow copy?
- Deep Copy and Shallow Copy
But all the answers seems very fuzzy to me.
Let's consider the example below:
const user = {
name: "James",
age: 33,
highlights: {
career: "Basketball player",
NBAChampion: [2012, 2013, 2016],
},
promotion: () => ("Get LeBron15 now!"),
};
const james = user;
const clone = { ...user };
const clone2 = Object.assign({}, user);
const clone3 = JSON.parse(JSON.stringify(user));
const clone4 = {
...user,
highlights: {
...user.highlights,
// I ment the it out, so now NBAChampion is a reference rather than copy!
// NBAChampion: [...user.highlights.NBAChampion]
}
};
user.age++;
user.highlights.career = "football player";
console.log('james', james, james === user, james == user);
console.log('clone', clone, clone === user, clone == user);
console.log('clone2', clone2, clone2 === user, clone2 == user);
console.log('clone3', clone3, clone3 === user, clone3 == user);
// console.log(clone3.promotion()); // My problem with clone3 is that function's been removed.
console.log('clone4', clone4, clone4 === user, clone4 == user);
I know this question has been discussed so many times, and I think I got a basic idea. There are some top rated answers I found from StackOverflow:
- What is the difference between a deep copy and a shallow copy?
- Deep Copy and Shallow Copy
But all the answers seems very fuzzy to me.
Let's consider the example below:
const user = {
name: "James",
age: 33,
highlights: {
career: "Basketball player",
NBAChampion: [2012, 2013, 2016],
},
promotion: () => ("Get LeBron15 now!"),
};
const james = user;
const clone = { ...user };
const clone2 = Object.assign({}, user);
const clone3 = JSON.parse(JSON.stringify(user));
const clone4 = {
...user,
highlights: {
...user.highlights,
// I ment the it out, so now NBAChampion is a reference rather than copy!
// NBAChampion: [...user.highlights.NBAChampion]
}
};
user.age++;
user.highlights.career = "football player";
console.log('james', james, james === user, james == user);
console.log('clone', clone, clone === user, clone == user);
console.log('clone2', clone2, clone2 === user, clone2 == user);
console.log('clone3', clone3, clone3 === user, clone3 == user);
// console.log(clone3.promotion()); // My problem with clone3 is that function's been removed.
console.log('clone4', clone4, clone4 === user, clone4 == user);
james
is a reference, it's always exactly same as user;clone
is a copy. Is it a shallow copy or deep copy? Thename
andage
are separated fromuser
, but thehighlights
is still a reference.clone2
behaves exactly the same asclone
.clone3
is converted from a string. Is it a deep copy? It is not a perfect clone, since functions (if there's any) are not save to convert this way.clone4
is copied every layer ofuser
, so I can call it a "deep copy".
However if the spread operator only creates deep copy sometimes, then how can I test if the new object is a deep copy or not?
Update: I mented NBAChampion
out in clone4
, so now NBAChampion is a reference rather than copy! If I push a new value in user.highlights.NBAChampion
, clone4
will also updates.
What should we call this type of object? It's neither a shallow nor deep copy.
Why does this matter?
React has a shouldComponentUpdate()
function which pares shallow copy. There is the link.
In React source code (Line: 356), the shallow parison is done like this:
shouldComponentUpdate(nextProps) {
return this.props.children !== nextProps.children;
}
In the code demo above, the 2nd and 3rd parameters in console.log
show the parison results between clone
and user
. However, only 1st copy returns true
. (Note: there is no different between strict parison and abstract parison)
If I apply shouldComponentUpdate
to the demo above, obviously only james
and user
will return true
. So james
is a shallow copy in Facebook's code. Then my questions are:
- Are reference and shallow copy exactly the same thing in JS? If not, why React do this?
- How can I test a object is a shallow copy or deep copy in my test case?
The question took me lots of time to craft. Any clear explanation with example is wele.
Thanks a lot.
-
clone
is a shallow copy. The primitive (and immutable) values of "name" and "age" are copies, yes, but then so is the reference to the "highlights" object; it's a copy of the reference. – Pointy Commented Mar 24, 2018 at 14:46 - "Are reference and shallow copy exactly the same thing in JS? If not, why React do this?" - references and shallow copies are certainly not the same thing. What exactly about React's shallow parison do you find unintuitive? Also, james is not a shallow copy of user. And the link to React's source code seems to be incorrect. – Alexey Lebedev Commented Mar 24, 2018 at 15:24
2 Answers
Reset to default 1Let's consider the example below
clone
and clone2
are shallow, only properties of original object are affected. clone3
and clone4
are deep.
However if the spread operator only creates deep copy sometimes, then how can I test if the new object is a deep copy or not?
It creates deep copy in case of clone4
- as long as the depth is controlled by a developer. Generally there's no need to test if an object is deep copy or just different in React, because this check is expensive and requires to traverse nested objects in both pared objects and pare them property by property.
Performance is the reason why React relies on immutability. If a new value isn't ===
equal, it's considered a different value.
So james is a shallow copy in Facebook's code.
It isn't. It's a reference that was assigned to another variable. It's still same object.
Are reference and shallow copy exactly the same thing in JS?
A reference isn't a copy. So it isn't shallow copy, too.
I mented NBAChampion out in clone4, so now NBAChampion is a reference rather than copy! If I push a new value in user.highlights.NBAChampion, clone4 will also updates. What should we call this type of object? It's neither a shallow nor deep copy.
It's just a copy. It doesn't have specific name, because there's rarely a need to do such selective copies. If the intention is to make it act like deep copy, it should be called a mistake.
A shallow copy of an object (or array) is a separate object with a matching set of property names and property values.
After making a shallow copy, a parison on a property-by-property basis of the two objects (the original and the copy) will show all property values being ===
.
For example:
let o1 = { a: 1, b: 2, c: { x: "hello", y: "world" } };
let o2 = {};
Object.keys(o1).forEach(propertyName => o2[propertyName] = o1[propertyName]);
Now if the property values of o1 and o2 are pared, they are of course ===
. In particular, property "c" of both objects will be a reference to that sub-object with "x" and "y" property names. However, paring o1
and o2
with either ==
or ===
will not show equality, because two distinct objects are never ==
to each other regardless of their contents.
A deep copy of an object is one where every object-valued property of the source is recursively deep-copied into the destination copy. Because new object values are of necessity created for the deep-copy target, those property values will not pare as ===
, because no two distinct objects can be ===
.
Making deep copies in a language like JavaScript can be problematic because the "graph" of references from property values can be circular, and because some property values may be functions. Generally a deep copy method has to make some assumptions on an application by application basis.
In my experience, the need to make a deep copy of an object is quite rare pared to making shallow copies.
Now, all that aside, parison between two object references as in your bit of React code:
shouldComponentUpdate(nextProps) {
return this.props.children !== nextProps.children;
}
has nothing to do with shallow or deep copies. That's a single parison between two object properties, both named "children". The objects referenced by this.props
and nextProps
may be different objects or they may be the same object, and one may be a shallow or deep copy of the other, but that makes no difference for that parison statement: all that's doing is paring the two particular "children" property values for strict inequality. (It's true that if nextProps
happens to be a shallow copy of this.props
, or vice-versa, that !==
parison would be false, but the parison doesn't have to know about the prior history of the two objects; it's just a single parison of two values.)
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744254359a4565322.html
评论列表(0条)