JS OOP Deep-Dive: Object.assign
[A full understanding of the above isn’t necessary for this article, but it would certainly help. See here for an explanation.]
We apply mixins via the
Object.assign method. As per MDN, the
Object.assign method “copies all enumerable own properties from one or more source objects to a target object,” like this:
First of all, let’s ensure we understand the official definition of
Object.assign. We covered ‘source’ and ‘target’ in the above code snippet. But what does ‘own enumerable’ mean?
‘Own’ is fairly straightforward — if you’ve gotten this far in the curriculum you know about prototypal delegation, which allows for an inheriting object to access the properties defined on the parent object. Those inherited properties are not considered the ‘own’ properties of the inheriting object, so they are not copied over to the target object via
Next: ‘enumerable.’ Scary big word. What does that mean? Well, MDN says: “Enumerable properties are those properties whose internal enumerable flag is set to true, which is the default for properties created via simple assignment or via a property initializer.” Oh, come on! First rule of definitions: don’t use that same word in the definition…if I didn’t know what it meant before, I still don’t. A simplified albeit roundabout understanding: ‘enumerable’ means that the property will show up if you iterate over the object using the
for..in loop or
Object.keys. And we know from MDN this is default behavior for most properties. Ok, enough of that, that’s an adequate understanding of ‘enumerable’ for our
Object.assign deep-dive. We’ll come back around to an example of enumerability at the end.
Now that we understand the definition of
Object.assign’s functionality, let’s dig a little deeper.
Question: what happens if a source object has the same property names as the target object’s properties? Answer: the property on the target object will be overridden, like this:
Also, we know from MDN’s definition that
Object.assign can accept multiple source object arguments. Question: what happens when there are multiple same-named properties in those arguments? Answer: we go according to the last source object argument with that same property name, like this:
When we say
Object.assign copies the properties from one object to another, what sort of ‘copy’ is this? You may remember about shallow copies versus deep copies from course 101. Turns out,
Object.assign creates shallow copies. Why should that matter to us?
Well, when comparing object creation patterns, we know that one disadvantage of the factory function model is that the methods defined on the factory function are copied over to each newly object created. And that’s bad for memory. We can demonstrate that we have distinct method objects on each new object by comparing their methods using the strict equality operator
false return value demonstrates that each object has their own (deep) distinct copy of the methods. Stated differently, each method points to a unique object in memory.
However, when we do the same comparison on methods copied over from mixins:
Here we get
true! [Exclamation point because this chance discovery confused me and inspired me to write this article.] This indicates that the two methods actually point to the same object in memory, even though we can clearly see that each object has their own (shallow) copy of the mixin method using
That’s good for memory, but a consequence of shallow copies is that any mutations to nested objects are reflected in other shallow copies of that mixin object.
A mutation can be made directly to the mixin:
Or a mutation can be made via the object’s own shallow copies:
Therefore, this behavior should be kept in mind when defining properties on mixin objects and sharing those properties via
You may think that mixin objects won’t have nested objects since we use mixins to share methods rather than nested objects— and you might be right. But, who’s to say how any given developer will define their mixin methods? Ultimately, leveraging shallow copies is beyond the scope of this article. Just beware of any nested objects defined on mixins, realizing that any mutations to those objects may lead to unintended bugs when copied via
Object.assign are enumerable.
Object.keys(only enumerable) and
Object.getOwnPropertyNames (also non-enumerable):
That concludes our foray into
Object.assign and mixins.