Even if materials_1 and materials_2 contain the same string values, they are actually two different Arrays. When you compare them, you are actually comparing references, that's why something like that will always be false, regardless of the content of the Arrays.
The same applies for [] == []. Each [] creates a new empty array and then you compare the references to them.
However, when you compare them to materials_3, which is a string (using ==), JS will call valueOf() on the other arrays to convert them to their primitive value. As the value returned from valueOf() is not a primitive either, it will call toString() on it, which returns "Hydrogen,Helium,Lithium". As strings are primitives, now the comparison will be done by value and return true.
You can find more about valueOf() on MDN:
JavaScript calls the valueOf method to convert an object to a primitive value. You rarely need to invoke the valueOfmethod yourself; JavaScript automatically invokes it when encountering an object where a primitive value is expected.
By default, the valueOfmethod is inherited by every object descended from Object. Every built-in core object overrides this method to return an appropriate value. If an object has no primitive value, valueOfreturns the object itself.
You can see that in action here:
function ObjectWrapper(actualObject) {
this.actualObject = actualObject || {};
}
ObjectWrapper.prototype.valueOf = function() {
console.log('valueOf');
return this.actualObject; // Not a primitive.
};
ObjectWrapper.prototype.toString = function() {
console.log('toString');
return this.actualObject.toString();
};
const foo = new ObjectWrapper([1, 2, 3]);
const bar = '1,2,3';
console.log(foo == bar);