3

I have some code here and I was wondering if it is the same thing or different. I am pretty sure these are both suppose to be the same but I wasnt sure if I was doing it right.

let zoneComment = updatedMap[action.comment.zone]
        ? [...updatedMap[action.comment.zone]] : [];

let zoneComment = updatedMap[action.comment.zone]
          ? Object.assign([], updatedMap[action.comment.zone]) : [];

If these are the same then which should I use or does it matter? I want to use best practice so if it is your OPINION of which is better then please state so.

2
  • Aside: if you're supporting IE11(ugh) both Object.assign and the spread operator aren't available. Otherwise they're both supported in all major browsers. "best practice" when working with objects is to use whatever is more clear to you/your team. Commented Aug 25, 2017 at 20:20
  • What is the relevance of the ternary operator and action.comment.zone index to your question? Why not ask about [...arr] versus Object.assign([], arr)? Commented Aug 25, 2017 at 20:22

3 Answers 3

6

In your particular case they are not the same.

The reason is that you have an array, not an object.

Doing ... on an array will spread out all the elements in the array (but not the properties)

Doing Object.assign expects an object so it will treat an array as an object and copy all enumerable own properties into it, not just the elements:

const a = [1, 2, 3];
a.test = 'example';

const one = [...a] // [1, 2, 3];
const two = Object.assign([], a); // { '0': 1, '1': 2, '2': 3, 'test': 'example' }

console.log('\none');
for (let prop in one) {
  console.log(prop);
}

console.log('\ntwo');
for (let prop in two) {
  console.log(prop);
}


However, if you compare the ... operator applied on an object with Object.assign, they are essentially the same:

// same result
const a = { name: 'test' }
console.log({ ...a })
console.log(Object.assign({}, a))

except ... always creates a new object but Object.assign also allows you to mutate an existing object.

// same result
const a = { name: 'test' }
const b = { ...a, name: 'change' };

console.log(a.name); // test
Object.assign(a, { name: 'change'})
console.log(a.name); // change

Keep in mind that Object.assign is already a part of the language whereas object spread is still only a proposal and would require a preprocessing step (transpilation) with a tool like babel.

Sign up to request clarification or add additional context in comments.

2 Comments

Amazing answer! And I am using react so I do have webpack running to trans-pile the code! Is there a time when it would be better to use the spread operator ?
Glad to help mate. As far as which one is better, I personally prefer the spread operator because it's a cleaner syntax in my own subjective view but other than that it comes down to your own personal preference or the preference of your team :)
4

To make it short, always use ... spread construction and never Object.assign on arrays.

Object.assign is intended for objects. Although arrays are objects, too, it will cause a certain effect on them which is useful virtually never.

Object.assign(obj1, obj2) gets values from all enumerable keys from obj2 and assigns them to obj1. Arrays are objects, and array indexes are object keys, in fact.

[...[1, 2, 3], ...[4, 5]] results in [1, 2, 3, 4, 5] array.

Object.assign([1, 2, 3], [4, 5]) results in [4, 5, 3] array, because values on 0 and 1 indexes in first array are overwritten with values from second array.

In the case when first array is empty, Object.assign([], arr) and [...arr] results are similar. However, the proper ES5 alternative to [...arr] is [].concat(arr) and not Object.assign([], arr).

Comments

2

Your question really bubbles down to:

Are [...arr] and Object.assign([], arr) providing the same result when arr is an array?

The answer is: usually, yes, but:

  • if arr is a sparse array that has no value for its last slot, then the length property of the result will not be the same in both cases: the spread syntax will maintain the same value for the length property, but Object.assign will produce an array with a length that corresponds to the index of the last used slot, plus one.

  • if arr is a sparse array (like what you get with Array(10)) then the spread syntax will create an array with undefined values at those indexes, so it will not be a sparse array. Object.assign on the other hand, will really keep those slots empty (non-existing).

  • if arr has custom enumerable properties, they will be copied by Object.assign, but not by the spread syntax.

Here is a demo of the first two of those differences:

var arr = ["abc"]
arr[2] = "def"; // leave slot 1 empty
arr.length = 4; // empty slot at index 3

var a = [...arr];
var b = Object.assign([], arr);
    
console.log(a.length, a instanceof Array); // 4, true
console.log(b.length, b instanceof Array); // 3, true
console.log('1' in arr); // false
console.log('1' in a); // true (undefined)
console.log('1' in b); // false

If however arr is a standard array (with no extra properties) and has all its slots filled, then both ways produce the same result:

Both return an array. [...arr] does this by definition, and Object.assign does this because its first argument is an array, and it is that object that it will return: mutated, but it's proto will not change. Although length is not an enumerable property, and Object.assign will not copy it, the behaviour of the first-argument array is that it will adapt its length attribute as the other properties are assigned to it.

Both take shallow copies.

Conclusion

If your array has custom properties you want to have copied, and it has no empty slots at the end: use Object.assign.

If your array has no custom properties (or you don't care about them) and does not have empty slots: use the spread syntax.

If your array has custom properties you want to have copied, and empty slots you want to maintain: neither method will do both of this. But with Object.assign it is easier to accomplish:

a = Object.assign([], arr, { length: arr.length });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.