Anything that is not one of the seven primitives is an object. That one type covers a huge family: plain objects, arrays, functions, dates, Maps, Sets, regexes, errors, and page elements. They all share object behavior, especially how they are copied and compared.
typeof {} // "object"
typeof [] // "object"
typeof new Date() // "object"
typeof new Map() // "object"
typeof /abc/ // "object"
typeof function() {} // "function" (still an object; typeof just labels it specially)
typeof reports "function" as a convenience, since "can I call this?" is such a common question.
A variable does not hold an object; it holds a reference to it. Copying the variable copies the reference:
const team = ["Ava", "Ben"];
const roster = team;
roster.push("Cal");
console.log(team); // ["Ava", "Ben", "Cal"] same array
const a = { x: 1 };
const b = { x: 1 };
const c = a;
a === b // false same contents, different objects
a === c // true same object
To compare contents, compare the parts you care about, or for simple data, compare their JSON:
JSON.stringify(a) === JSON.stringify(b) // true (only safe for simple data in the same key order)
Spread (and Object.assign, slice) make a new outer object, but anything nested inside is still shared:
const user = { name: "Ava", tags: ["js"] };
const copy = { ...user };
copy.name = "Zed"; // fine: top-level property is independent
copy.tags.push("css"); // changes the SHARED inner array
console.log(user.name, user.tags);
structuredClone copies everything, all the way down:
const user = { name: "Ava", tags: ["js"], joined: new Date(2026, 0, 5) };
const deep = structuredClone(user);
deep.tags.push("css");
console.log(user.tags); // ["js"] untouched
console.log(deep.joined instanceof Date); // true still a real Date
| Method | Depth | Notes |
|---|---|---|
| b = a | No copy | Same object |
| { ...a } / [...a] | Shallow | Nested objects shared |
| JSON.parse(JSON.stringify(a)) | Deep | Loses dates, functions, undefined |
| structuredClone(a) | Deep | Keeps dates, Maps, Sets; cannot copy functions |
JavaScript comes with many kinds of object ready to use. You have met most of them already:
| Object | What it holds | Where |
|---|---|---|
| Array | Values in order, by numeric index | JS Arrays |
| Map | Key-value pairs; keys can be any type | JS Maps |
| Set | Values that can each appear only once | JS Sets |
| WeakMap / WeakSet | A Map / Set that lets unused objects be cleaned up | JS Map WeakMap, JS Set WeakSet |
| Date | A moment in time | JS Dates |
| RegExp | A regular expression | JS RegExp |
| Math | Math constants and functions like Math.PI and Math.random() | JS Math |
| Error | Describes something that went wrong | JS Errors |
| JSON | Converts between objects and JSON text | Used in this lesson |
| Promise | A value that will arrive later | Not covered in this course |
| Int8Array, Float64Array, ... | Typed arrays: fixed-size lists of one kind of number | Below |
A typed array stores numbers of only one exact size. An Int8Array holds whole numbers from -128 to 127, and anything outside that range wraps around:
const bytes = new Int8Array([100, 200, -1]);
console.log(bytes[0], bytes[1], bytes[2]); // 200 does not fit, so it wraps
console.log(bytes.length, Array.isArray(bytes));
Typed arrays are used for things like image pixels and sound, where memory and speed matter. W3Schools lists Int8Array, Int16Array, Int32Array, Float16Array, Float32Array, Float64Array, and BigInt64Array. There are also unsigned versions for numbers that are never negative, like Uint8Array (0 to 255).
Math and JSON are single ready-made objects that you use directly: Math.PI, JSON.stringify(x). new Math() is TypeError: Math is not a constructor (and the same for JSON). Compare new Date() and new Map(), which make a new object each time.
Array.isArray(new Map()) is false, and a Map has size, not length.
Array.isArray(x) // arrays (the most reliable check)
x instanceof Date // made by the Date constructor?
x instanceof Map // ...Map, Set, RegExp, Error, your own classes
x !== null && typeof x === "object" // "is it any object?"
A universal trick reports the built-in kind of any value:
const kind = v => Object.prototype.toString.call(v).slice(8, -1);
kind([]) // "Array"
kind(new Date()) // "Date"
kind(null) // "Null"
kind(/x/) // "RegExp"
kind(42) // "Number"
What does this log?
const a = [1, 2];
const b = a;
const c = [...a];
b.push(3);
console.log(a.length, c.length);
3 2. b is the same array as a, so the push changes a. c is a separate copy made before the push.Which one throws a TypeError?
new Date()new Map()new Math()new Set()Math is a single built-in object, not a constructor: TypeError: Math is not a constructor.typeof [] and typeof new Map() both give "object". Name a check that tells each one apart.
Array.isArray(x) for arrays and x instanceof Map for Maps. The kind function above works for both: it gives "Array" and "Map".structuredClone and confirm the original is safeinstanceof to tell a Date, a Map, and an array apartkind example with its Try it Yourself button and run it on ten different values