In JavaScript, all values that are not primitive (string, number, boolean, bigint, symbol, null, or undefined) are objects. This includes arrays, functions, and regular expressions. You need to be aware of referential equality when comparing objects.

Referential equality is when two or more variables point to the same object in memory. The variables are considered referentially equal when compared because they reference the same object.

const operatingSystem = {
name: "Windows",
developer: "Microsoft",
};

const operatingSystem2 = operatingSystem;

console.log(operatingSystem === operatingSystem2); // true

In this code snippet, the operatingSystem constant points to the object with the name and developer properties. The operatingSystem2 constant points to the same object as the operatingSystem constant. It does not point to a copy.

An arrow points from a rectangle labelled 'operatingSystem' to a JavaScript object. A separate arrow points from a rectangle labelled 'operatingSystem2' to the same JavaScript object.
A diagram that represents referential equality in JavaScript.

On the contrary, you may have two objects with the exact same shape, but because they are separate objects in memory, they are not considered referentially equal. Think of identical twins. They may have the same DNA, but they are still two separate people!

const operatingSystem = {
name: "Windows",
developer: "Microsoft",
};

const operatingSystem2 = {
name: "Windows",
developer: "Microsoft",
};

console.log(operatingSystem === operatingSystem2); // false
An arrow points from a rectangle labelled 'operatingSystem' to a JavaScript object. A separate arrow points from a rectangle labelled 'operatingSystem2' to a JavaScript object with the same shape as the other one.
A diagram that represents two variables pointing to two separate objects in JavaScript.

To check if two objects have the same shape, you need to check that they have the same set of key-value pairs. This is more complicated than it sounds, because:

Here’s a solution I came up with after a bit of back-and-forth with ChatGPT. If you spot a flaw in it, I’d love to hear from you!

function haveSameShape(obj1, obj2) {
// If the objects are referentially equal or both `null`, return `true`.
if (obj1 === obj2) return true;

// If only one of the objects is `null`, return `false`.
if (obj1 === null || obj2 === null) return false;

const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);

// If the objects don't have the same set of properties, return `false`.
const allKeys = new Set([...keys1, ...keys2]);
if (allKeys.size !== keys1.length) return false;

for (const key of keys1) {
const type1 = typeof obj1[key];
const type2 = typeof obj2[key];

// If the values don't have the same data type, return `false`.
if (type1 !== type2) return false;

// If the values are objects with different shapes, return `false`.
const areObjects = type1 === "object" || type1 === "function";
if (areObjects && !haveSameShape(obj1[key], obj2[key])) return false;
}

// Otherwise, return `true`.
return true;
}

In summary:

  • All values that are not primitive are objects.
  • Two or more variables are considered referentially equal if they point to the same object in memory.
  • Two or more objects may have the exact same shape, but this does not mean they are the same object in memory.
  • To check if two or more objects have the same shape, you need to check that they have the same set of key-value pairs. If any of the values are objects, you need to check them recursively.