Understanding the difference depends on understanding the fact that
splice will change the length of the array you’re calling it on. This isn’t going to be easy for me to write about, and it’s going to be just as hard for you to understand, so you need to really think this through. Let’s first read the loop construct for the code that doesn’t work:
for (var x = 0; x < newArr.length; x++)
x starts at 0. While
x is less than the length of the array at
newArr, keep running the loop. At the end of each loop, increment
x by 1. Note that the loop gets newArr.length at each iteration. If the length of the array changes, so do the loop’s parameters.
Now think about what happens in this loop the first time it gets a match. Let’s say we’ve called
destroyer([2, 3, 2, 3], 2, 3), so
[2,3]. We start both loops at 0, and the first number we want to destroy is 2 (
arr is 2);
// x === 0 and i === 0
if (newArr[x] == arr[i]) newArr.splice(x,1);
// same thing as
if(2 == 2) newArr.splice(x,1);
// so the array gets spliced
[2,3,2,3].splice(0, 1); // [3,2,3] <= newArr has been changed.
After the first item has been destroyed,
x increments to 1. The same thing happens because
newArr is 2, so it happens to delete another number. Now the array is
[3,3]. The length of the array started at 4, but now it’s 2.
x increments to 3, and while this wouldn’t have ended the loop when we started, since the array is shorter the inner loop ends.
The outer loop increments -
i is 1. We’re now looking to destroy 3 since that’s the value at
arr. This is where the code breaks down.
// x === 0 and i === 1
if(newArr[x] == arr[i]) newArr.splice(x,1);
// same thing as
if(3 == 3) newArr.splice(x,1);
// the array gets spliced again
[3,3].splice(0, 1); //  <= newArr has been changed
newArr's length is 1. The array only contains the number 3, which should be deleted, right?
newArr has only one value, its index is 0. We just got done destroying the 0th item. The loop increments and we’re now looking at
x === 1 and
i === 1. Both loops are done and the function returns an array with a single value,
The reason the other code works is because you’re editing
newArr from the end and decrementing the inner loop.
newArr's length still changes, but since we’re counting from the back and working towards 0, it doesn’t matter. Imagine chopping down a tree by chunks. The broken code is the same as starting from the roots and moving up. As you chop a chunk, you shorten the tree from the bottom. The code that works is like chopping chunks off the tree from the top.
You’re probably not going to understand the problem just by reading this mess. I know that I wouldn’t. The only way this is going to click is if you walk through to code yourself, methodically and slowly. Talk out loud. Pretend you’re explaining the code to a small child or a rubber duck. This is how you really learn to code, and there are no shortcuts.