Can arrays be mutated if they're assigned as values to variables in function scope?

Dear people,

Questions

  1. Are arrays mutated if they’re assigned as values to variables in function scope and such variables are eventually mutated?
  2. If so, does that mean that, if you want to leave original arrays unchanged, it’s bad practice to assign arrays as values to variables in function scope?
  3. Does this phenomenon relate to the concept of “pass by reference”?

Explanation

  • Say I have an array “globalArray”.
  • Then I declare a function “randomFunction” which takes an array as argument.
  • Then, in the function body, I i) declare a constant variable “innerArray” in function scope; ii) assign “innerArray” the array being called as value; and iii) push elements into “innerArray”.
  • Then I call randomFunction(globalArray).
  • As result, I would expect that innerArray would be modified as per my function instructions and globalArray would be left unchanged (because I’ve taken care not to work directly on “globalArray”).

Yet both innerArray and globalArray end up changed in exactly the same way.

const globalArray = [1, 2, 3];

function randomFunction(array) {
  const innerArray = array;
  innerArray.push(4, 5, 6);
  return innerArray;
}

console.log(randomFunction(globalArray)); // [1, 2, 3, 4, 5, 6]
console.log(globalArray); // [1, 2, 3, 4, 5, 6] (was hoping it would be [1, 2, 3])

Thank you for your kind help :gorilla:

It might seem like that is copying the array, but in fact it is just a reference to the same one. Any mutation will take place on that array. To make a copy, use Array.slice().

Another approach would be take in an array, then return it as an expression.

const extendArray = (array, extend) => array.concat(extend)
array = [1 ,2 ,3]
console.log(extendArray(array, [4, 5, 6]))
//<- [ 1, 2, 3, 4, 5, 6 ]
console.log(array)
//<- [ 1, 2, 3 ]
1 Like

Expanding on what @mtf said, usually cloning arrays in JavaScript will result in shallow copies of the original array. Take this example:

let myArray = [[1,2],[3,4]];
let cloneArray = myArray.slice();
cloneArray[0][0] = 'A';

console.log(myArray);      // returns [ [ 'A', 2 ], [ 3, 4 ] ]
console.log(cloneArray);   // Also returns [ [ 'A', 2 ], [ 3, 4 ] ]

This is why the copy is called “shallow”, because if the original array contains arrays/objects, those will be passed by reference. So it depends on what you are trying to do! For your example, the method presented is totally valid. Good luck :+1:

PS: and in case you were wondering of the new ES6 way of cloning arrays with the spread operator like let cloneArray = [...originalArray];, that will also produce a shallow copy of the original

1 Like

Thanks for the deep dive into the shallow copy. I didn’t want to go there since the extend function doesn’t clone. It extends. By my logic that means the original array is still intact, even though is also assigned in the expression. Is that a shallow copy, too?

1 Like

I wrote the experiment on repl.it so that we can all test out hypothesis. Here’s the link: https://repl.it/@fedGL/ShallowCopiesTestyTest

It would seem using an auxiliary function with .concat() also produces shallow copies of arrays.

1 Like

And for the reason you earlier brought up… reference.

This has come up before in regards to making deep copies of arrays and objects but it was a long time ago. Might be worth revisiting as a UGC question.


Might be worth a look at what @ionatan posted on the subject of nested arrays and their depth. Will try to dig it up, unless you find it first.

Or, it might have been a challenge way back when. @alexc might remember if that was the case.

1 Like

So far I can only find a post on tree depth. Still searching.


Update

No luck in the forums, but found this on MDN…

Array.prototype.flat()

Just for the fun of it…

const flatten = function (arr) {
  const f = a => {
    if (! a.some(x => Array.isArray(x))) {
      return a
    }
    levels.count += 1
    return f(a.reduce((u, v) => u.concat(v), []))  //  *
  } 
  const levels = {count: 1}
  return [f(arr), levels.count]
}
const array = [1, [2, [3, [4, [5, [6, [7, [8]]]]]]]]
const result = flatten(array)
console.log(...result)
// * ibid MDN
[ 1, 2, 3, 4, 5, 6, 7, 8 ] 8
const other = [1, [2, [3, [4, [5, [6, [7, [8], 9], 10], 11], 12], 13], 14], 15]
result = flatten(other)
console.log(...result)
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ] 8