Let me attempt to improve upon Fowler's ValueObject pattern in JS, and tell you the upsides:
function Point(x, y) {
const hash = Point.__hash(x, y);
const cached = Point.__cache.get(hash);
if (cached) return cached;
const newPoint = {get x() { return x; }, get y() { return y; } };
Point.__cache.set(hash, newPoint);
return newPoint;
}
Point.__hash = function(ptx, pty) {
return `${ptx}|${pty}`;
}
Point.__cache = new WeakMap();
This does what you would expect a ValueObject (or an algebraic type or struct depending on the languages you normally use) to do in another non-JS language - it interns all equivalent objects so that simple reference equality is sufficient to determine equality - rendering the nonstandard "equals" method unneeded. This also solves all the issues with ".includes" and so forth, again, because the built in reference equality is sufficient to determine equality. This also has much better memory characteristics in a system where many equivalent objects are created, as only one copy is ever stored in memory.
The only drawback is the small overhead of the WeakMap used to cache all the references and the overhead of "hashing" at object creation time - neither of which should be noticeable in most applications, and the memory benefits should outweigh these concerns in most performance applications regardless.