Beginner
+25 XP

👋 Start learning JavaScript right now — for free!

📖

Object Type — Nuances and Pitfalls

Why null is object, arrays are object, and how not to break code when copying

🎒

Object — a backpack with labeled pockets

A primitive (number, string) is one simple thing: a coin, a key. An object is a backpack with named pockets. Pocket 'name' → 'John', pocket 'age' → 25. Inside can be other backpacks. That's the main feature — object stores STRUCTURED data with named fields.

What object really is

Object is a collection of key: value pairs. Keys are strings (or Symbol), values are any type.

js
const user = {
  name: "John",       // key: 'name', value: string
  age: 25,            // key: 'age', value: number
  isStudent: true,    // key: 'isStudent', value: boolean
  address: {          // key: 'address', value: another object!
    city: "London",
    zip: "SW1A",
  },
};

// Accessing properties
console.log(user.name);          // "John" — dot notation
console.log(user["name"]);       // "John" — bracket notation
console.log(user.address.city);  // "London" — nested access

Gotcha #1: typeof null === "object"

This is one of JavaScript's most famous bugs:

js
console.log(typeof null);    // "object" ← BUG IN JS ITSELF
console.log(typeof {});      // "object" — correct here
console.log(typeof []);      // "object" — arrays are also object!

typeof null === "object" — this is a bug from 1995 (first week of JavaScript's existence). Null is not an object, but it can't be fixed: too much old code relies on it.

How to properly check for null:

js
// WRONG:
if (typeof value === "object") { ... } // catches null too!

// CORRECT:
if (value !== null && typeof value === "object") { ... }

Gotcha #2: Arrays are also object

js
let fruits = ["apple", "banana"];

console.log(typeof fruits);          // "object" — not "array"!
console.log(Array.isArray(fruits));  // true — the correct check
console.log(Array.isArray({}));      // false

// Why? Array in JS is an object with special numeric keys:
let arr = ["a", "b", "c"];
// Actually: { 0: "a", 1: "b", 2: "c", length: 3 }
console.log(arr[0]);      // "a"
console.log(arr.length);  // 3

Rule: to check if something is an array — always use Array.isArray(value), not typeof.

Gotcha #3: Primitive vs Reference

This is THE MOST IMPORTANT object gotcha. Primitives store values, objects store references to a memory location.

js
// Primitives — copied by value
let a = 5;
let b = a;   // b gets a COPY of value 5
b = 10;
console.log(a); // 5 — a did NOT change!

// Objects — copied by reference
let user1 = { name: "John" };
let user2 = user1;   // user2 points to THE SAME object!
user2.name = "Maria";
console.log(user1.name); // "Maria" — user1 ALSO changed!

user2 = user1 does not copy the object — only passes the address in memory. Both variables point to the same place.

javascript
💬

{ ...original } — spread operator. It copies ALL properties into a new object. This is a 'shallow copy'.

Gotcha #4: Objects compared by reference

js
// Primitives compared by VALUE
console.log(5 === 5);           // true — same values
console.log("JS" === "JS");     // true

// Objects compared by REFERENCE (memory address)
console.log({} === {});         // false — different objects!
console.log([] === []);         // false — different arrays!

let a = { x: 1 };
let b = a;          // b points to the SAME object
console.log(a === b); // true — same address!

{} === {}false, because these are two different objects in different memory locations, even if they look identical.

How to compare contents? Convert to string: JSON.stringify(obj1) === JSON.stringify(obj2)

Gotcha #5: Shallow copy vs Deep copy

js
// Shallow copy — spread
let user = { name: "John", address: { city: "London" } };
let copy = { ...user };

copy.name = "Maria";       // OK — primitive was copied
copy.address.city = "Paris"; // DANGEROUS!

console.log(user.name);          // "John" — unchanged ✓
console.log(user.address.city);  // "Paris" — CHANGED! ✗
// Spread only copies the first level!
// address — nested object, passed by reference.

// Deep copy — via JSON
let deepCopy = JSON.parse(JSON.stringify(user));
deepCopy.address.city = "Berlin";
console.log(user.address.city);  // "Paris" — unchanged ✓

Spread ({...obj}) — shallow copy. For nested objects — use JSON.parse(JSON.stringify(obj)) or structuredClone(obj) (modern method).

Two most common object mistakes: 1. let copy = obj — you think you copied, but both variables point to the same object. 2. typeof null === 'object' — checking the type and forgetting to exclude null. Memorize: for copying — spread {...obj}, for null check — value !== null.

javascript
💬

structuredClone() — built-in function for deep copying. Supported in all modern browsers.

Comments

Log In or Start to leave a comment.