Why does it need the .slice()?

I don’t understand why we need to add the .slice() as we create the local variable.
Can’t we just copy the value of str2 in localsStr?

considering this:

let str2 = [1, 2, 3, 4];
let localStr = str2;

isn’t this statement true?:

localStr === str2.slice();

thanks for you guidance! :slight_smile:
Eric

Link to the challenge:

So let me share my “mental model” of some javascript basics, see if it helps.

First, think of variables as labels we use to refer to data. We can “wire” the variable to something by assigning data to it, but what js is actually doing is placing that variable in a "lookup table"and pointing it at a particular memory location, where js has arbitrarily placed our data.

Second, “data” refers to either things (primitives, like a number or a string) or boxes of things (non-primitives, like arrays, objects and functions). When we wire our variable to these, we either point at the thing itself, or to that container of things. And when we do something like const myArr2 = myArr, we are simply running another wire to the same box of things.

If we tell one of those boxes to change the contents of the box, then the contents of the box are changed. but both variables are wired to the same box, so both see those changes.

If, instead, we assign the second variable to point to a copy of the box (which is what slice is doing), then we have two separate boxes that look the same, but if we change one the other won’t see it.

4 Likes

Thanks again for you well explained reply!

I get why the slice() method is important. I’ve heard about that copy/pointer business when I did the cs50 course (in C language).

But because I always like to test things out, I wrote some lines of code in my ‘runJs’ app:

let arr1 = [1, 2, 3, 4 ];
let arr2 = arr1;
arr2 = [5, 6, 7, 8 ];

console.log(arr2); // [5, 6, 7, 8 ]
console.log(arr1); // [1, 2, 3, 4 ]

and the arr1 keeps it’s original value even after arr2’s value is modified.

Does ‘runJs’ do hidden things under the hood? Or is it just good practice/habit to not simply point to a variable because it wont work in other languages?

Maybe I’m simply missing something obvious here and my message won’t make any sence… I’m sorry if that’s the case! :slight_smile: I just want to make sur I understand things properly.

Thanks again for taking time to reply!

1 Like

Try

let arr1 = [1, 2, 3, 4 ];
let arr2 = arr1.splice(2);
// Or let arr2 = arr1.push(5);
// Or let arr2 = arr1.shift();

console.log(arr2);
console.log(arr1);
1 Like

This is perfect! I’m so glad you explored and asked, the marks of a soon-to-be master coder!

So in the first line, you created an array in memory, and then pointed the arr1 variable to that memory location. Easy peasy.

In the second line, you’ve asked javascript to wire a second variable to that same reference. arr2 now points to the same place in memory.

But there’s a little more to note: once we have placed something in memory, that thing doesn’t change. If we assign a string to a variable, and then update that variable with the uppercase version of that string, we are creating a new thing, in a new memory slot, and moving the variables wire to point to it.

In the case of arrays or objects, the actual container (the array-holder itself, not the data inside it) is the immutable bit. So long as we change stuff insidethe array without changing the reference itself, both refer to the same thing.

If your line three had looked like this:

for(let i=0; i<arr2.length; i++){
  // this next line!!
  arr2[i] = i;
}

console.log(arr1);

So in this, we aren’t replacing the value in arr2 - we are re-wiring items inside the array (replacing things in the box, rather than creating a new box).

So we can mutate the array (because, from C, you know it as an array of pointers) but doing what you did:

arr2 = [5, 6, 7, 8 ];

You are saying “create a new array containing these four items.” With this, you fill a new memory location, point arr2 there but leave arr1 pointing at the original. You’ve broken the “reference connection” between the two.

I now consider you my friend. I hope I’ll get to pay you a beer some day.

Over the last year, I spent most of my time learning html, css, js. Like you can imagine, I spent heaps of time on stackoverflow trying to find answers to my questions. Questions that I couls never manage to ask properly. I had to learn to stop being polite and friendly. I now stopped trying and just go elsewhere because it became way too stressfull to ask questions there.

It’s the first time that someone actually took the necessary time to answer my question and try to help me understand. But I suppose that’s what freecodecamp is all about! I hope I’ll get to help others the same way!

I went back to runJS and was flabergasted to realise that what you explained is true. Not that I doubted you… I just find it fascinating. I feel now that I understant EXACTLY how variables exist and live inside the memory. I suppose there is plenty more that I don’t know about… but still…

Your explanation is great. Thank you so much for taking the time to write it well and clearly! I can totaly imagine how this will prevent me from living a debugging nightmare one day.

Now, a bunch of questions pop up. like if arr1 is a boolean. How can I prevent the phenomenon from happening when I modify arr2… runJS here I come, I will totaly explore what happens with all the variable types.

Thanks again, you are a legend!!

1 Like

It’s a common thought that primitive and non-primitive data are treated differently in javascript, but it just ain’t so.

Primitives are things - strings, numbers, Booleans, null, undefined and the like. These are simple things, and there are two things to note: they don’t contain other things, and they’re immutable. Once they’ve been placed in memory and as long as something is referencing them, they do not change. ever.

Non-primitives are collections of things - arrays, objects, Sets, Maps and the like. These are “complex” things, in that they contain other things, but it is important to note that, once a non-primitive has been placed in memory, that non-primitive is also immutable. While we can change the contents of that thing, the thing itself, the initial reference for the container, that is fixed once in memory and for as long as something is aware of it.

So if we have two variables, like this:

let a = "Euripides";
let b = a;
// at this point, they both point at the 
//   same memory location!

// but what happens next?
b = b.toUpperCase();

With that last line, we explicitly replace b with a new value, at a different memory location, leaving a looking at the original. So because primitives are immutable, when we evaluate the new result it will be a completely separate memory chunk.

With non-primitives, the result is similar:

let a = ["a","b","c"]
let b = a;
// at this moment, both point to the container,
// and to the same container.

// if we explicitly assign something:
b = b.map( char => char.toUpperCase() )

Again, we used the assignment operator (the =) in that last line, explicitly replacing b with a new array*. They are separate now, with no knowledge of each other.

Non-primitives differ from primitives only in that you can muck with the things they contain - because, while the container is immutable, it’s contents may not be.

It’s a bit confusing - an array of strings, internally, is simply an array container with a bunch of "pointers"telling us where to find each array item in memory. So While the container may be immutable, the outer box, we can still point those internal pointers at different memory locations, mutating the innards of that array.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.