Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Array.prototype.map() mutate the array it invokes on?

Mozilla said:

map does not mutate the array on which it is called (although callback, if invoked, may do so).

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Specifically, the third argument the callback was passed is:

The array map was called upon.

Which I assumed that means the array location in memory is copy into the callback by reference.

So by mutating the third argument, we should mutate the original array, but the following two pieces of snippet give conflicting result:

case 1, re-assigning the third argument did not mutate the original array:

var A = [1, 2, 3];
A.map((v,i,_A) => { 
  console.log("_A is the array map invoked on: ", _A===A); // true
  _A = []; // reassign an empty array
  return v;
});
console.log("'A' did not mutate:", A) // [1, 2, 3] did not mutate

case 2, setting the third argument to length of zero mutates the original array:

var B = [1, 2, 3];
B.map((v,i,_B) => { 
  console.log("_B is the array map invoked on: ", _B===B); // true
  _B.length = 0; // clearing the array by setting it zero length
  return v;
});
console.log("'B' has mutated:", B) // [] mutated

Clearly in the second case the original array is mutated (the iterator only execute once). But what am I missing ? Why the conflicting result ?

like image 619
apollo Avatar asked Dec 17 '18 04:12

apollo


People also ask

Does map mutate the array?

map does not mutate the array on which it is called (although callbackFn , if invoked, may do so). The range of elements processed by map is set before the first invocation of callbackFn .

Does array map change the original array?

map() creates a new array from calling a function for every array element. map() calls a function once for each element in an array. map() does not execute the function for empty elements. map() does not change the original array.

Can we mutate an array?

Arrays in JavaScript are just objects, which means they can be mutated.


2 Answers

In case 1, you're only reassigning - this is separate from mutation. Mutation will only occur if you have syntax like obj.prop = <someExpression>. This is what you're doing in case 2 with

_B.length = 0;

The object in memory that _B references will have its .length property mutated. But just reassigning a variable name, like _A = [], won't change whatever _A happened to refer to before - the object that was previously referenced by _A will be ignored (left unchanged), and future references to _A in the scope will refer to whatever was just assigned to _A.

This doesn't really have anything to do with Array.prototype.map - this is the same sort of behavior you would see for any Javascript object or variable name, eg:

const obj = { foo: 'foo' };
let anotherObj = obj; // references the original object
anotherObj = {};
// a new object is created in memory and assigned to the variable name `anotherObj`
// but this does not mutate whatever anotherObj happened to refer to before
console.log(obj);
like image 109
CertainPerformance Avatar answered Sep 27 '22 21:09

CertainPerformance


In JavaScript, things aren’t exactly pass-by reference as you may be familiar with. A local copy of the reference to A is created in the local scope of the callback. See this answer for more info: Does Javascript pass by reference?

Therefore, when you reassign _A to an empty array, you are just telling the reference copy to point to a new empty array. However, the old reference to A still exists and is untouched.

When you mutate length, you are actually mutation the object the _A refers to.

like image 33
Rafael Abbondanza Avatar answered Sep 27 '22 20:09

Rafael Abbondanza