Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialAlex Hort-Francis
17,074 PointsOn spread syntax and multi-dimensional arrays
Exploring more about this (from the teacher's notes):
const brass = [ ['trumpet'], ['tuba'], ['trombone'] ];
const instruments = [...brass];
instruments.shift().shift();
// "trumpet"
// Notice how the brass array is also affected by this:
brass
// [ [], ["tuba"], ["trombone"] ]
There seems to be something going on to do with 'shallow' copies, and the difference between copies and references: https://stackoverflow.com/questions/43421704/why-is-a-spread-element-unsuitable-for-copying-multidimensional-arrays
Quite an interesting but confusing topic; I wonder if anyone has a good way of explaining this.
Thanks! :)
1 Answer
Juan Luna Ramirez
9,038 PointsWhen you refer to an object like an array (either by a variable name or an object property name) you are pointing to the original object/array, often referred to as passing by reference.
// variable alex points to this new array we created
const alex = ['alex', 'francis']
// variable person points to thesame array that alex variable points to.
// So if we change something using the person variable then we are actually
// changing the original array created above.
const person = alex
person[0] = 'juan'
person[1] = 'luna'
console.log(alex) // ['juan', 'luna'] oops, not alex anymore
This is simple enough, until the pointer to that original object/array gets passed around and stored in places where you don't know for sure which object/array a variable or property is pointing to.
// languages points to this newly created array
const languages = ['javascript']
const alexProfile = {
name: ['alex', 'francis'],
programmingLanguages: languages, // points to the array created above
}
const juanProfile = {
name: ['juan', 'luna'],
programmingLanguages: languages, // also points to the SAME array created above
}
// somewhere else in your code that updates a person's profile.
// save alex's languages for convenience to a variable.
// BUT note that this variable ultimately points to the SAME array created above
const alexLanguages = alexProfile.programmingLanguages
// alex learns python so we have to update his profile
alexLanguages.push('python')
console.log(alexProfile.programmingLanguages) // ["javascript", "python"] sweet! works!
// but...
console.log(juanProfile.programmingLanguages) // ["javascript", "python"] Wait, noooo!
To prevent this you'll often want to make a copy. There are several ways to do this, including the spread operator, functions (third-party or built-in) or manually duplicating the data.
Again, the tricky bit as before is if you have nested objects/arrays as in the teacher notes example. The spread operator only copies the top level. Which means that any nested objects/array will still point to the original object/array. If you wanted a complete copy you would have to go through all the nested objects/arrays and make a copy of those. This is often referred to a deep clone. This is why developers opt to use a library like lodash. You can use their cloneDeep
function and it will create a deep clone for you! hehe https://lodash.com/docs#cloneDeep. You can also create you own deep clone for this example using the built in map
or reduce
function if you wanted to.
Some comments on the example
const trumpetArray = ['trumpet']
const tubaArray = ['tuba']
const tromboneArray = ['trombone']
const brass = [ trumpetArray, tubaArray, tromboneArray ];
// this is just pointing to the original brass array, so changing anything in
// notACopy will result in brass being changed as we've seen before
const notACopy = brass
// copies ONE level
const instruments = [...brass]
// to get the same result you can declare a brand new array
const brassCopy = [ trumpetArray, tubaArray, tromboneArray ]
// So you can add and remove elements from instruments and brassCopy without
// affecting bass.
brassCopy.shift()
console.log(brassCopy) // [["tuba"], ["trombone"]] OK!
console.log(brass) // [["trumpet"], ["tuba"], ["trombone"]] YES, untouched!
// but as soon as you touch any of the arrays inside, for example the
// trumpetArray, then you are changing the original array trumpetArray
// is pointing to.
instruments[0].shift()
console.log(brass) // [[], ["tuba"], ["trombone"]] awww :(
This can definitely be confusing. One great resource that I think explains this well is https://justjavascript.com/ if you want to spend some more time on this which I believe will help you a lot!
Alex Hort-Francis
17,074 PointsAlex Hort-Francis
17,074 PointsThat's really well explained, thanks!