How does join() produce different results depending on the arguments?

In the first example you are not calling .join on the array but on arguments. That variable will be populated with an array-like object that has an array as first index, in essence, you are calling the equivalent of:

let myArguments = [[1, 2, 3]];

console.log(myArguments.join("_"));

instead of what you do (the equivalent of) in the second example:

let myArguments = [1, 2, 3];

console.log(myArguments.join("_"));

In your code, you only have one argument in the first example, that being an array. Joining a single element will remove the brackets:

var test = function() {
  var args = Array.prototype.join.call(arguments,"_");
  return args
};

console.log(test([1,2,3])) // args = [[1,2,3]]
console.log(test(1,2,3)) // args = [1,2,3]

console.log([[1,2,3]].join('_'))
console.log([1,2,3].join('_'))

Another way to look at this is to provide another array as an argument to test():

var test = function() {
  var args = Array.prototype.join.call(arguments,"_");
  return args
};

console.log(test([1,2,3], [4,5,6]))

The first one, first argument is [1,2,3] which is joined with the second argument, which is nothing -> output is [1,2,3].toString().

Second call, it's actually joining all the 3 arguments, resulting in outputting 1_2_3.


When you pass an array to test, the arguments object becomes an array of arrays, not a plain array of plain values. It's the difference between

arguments = [[1,2,3]];

and

arguments = [1,2,3];

When you call .join on an array of arrays, each inner array gets implicitly coerced to a string first, resulting in '1,2,3' - the values of the inner arrays do not get .joined by the delimiter, only the immediate children of the outer array get .joined by the delimiter.