JavaScript/JavaScript Fundamentals for Frontend Interviews/Objects, Arrays & Reference Semantics

Objects, Arrays & Reference Semantics

Medium100% Free~20 mins#objects#arrays#references#mutation#shallow-copy#deep-clone#equality
Key Learning Objectives
✓

Differentiate between primitive values stored on the stack and composite references stored on the heap.

✓

Understand reference equality (===) versus structural equality.

✓

Master shallow copying mechanisms (Object.assign, object spread) and recognize shared nested reference traps.

✓

Learn safe deep cloning techniques including structuredClone() and its limitations with functions and DOM nodes.

The Interview Problem

What is logged to the console when the following code executes, and how do object aliasing and shallow copying affect nested property mutation?

1const original = {
2 id: 1,
3 tags: ['js', 'web'],
4 meta: { views: 100 },
5};
6
7const shallowCopy = { ...original };
8const alias = original;
9
10shallowCopy.id = 2;
11shallowCopy.tags.push('v8');
12shallowCopy.meta.views += 50;
13
14alias.id = 3;
15
16const res1 = original.id;
17const res2 = original.tags.length;
18const res3 = original.meta.views;
19const res4 = original === shallowCopy;
20const res5 = original.meta === shallowCopy.meta;
21
22console.log(res1, res2, res3, res4, res5);
Predict Console Output
Interactive Challenge

Select the option that matches what standard ECMAScript prints to the console:

3 3 150 false true

1 2 100 false false

3 2 100 true true

2 3 150 false true

V8 Engine Execution Trace
Step 1 of 9 (Line 1)

Global Execution Context allocates original in heap memory at HeapRef#1. Its properties tags (HeapRef#2) and meta (HeapRef#3) are allocated as separate nested heap entities.

Call Stack (Top = Active)
Global Execution Context
Lexical Scope / Bindings
original:HeapRef#1 ({ id: 1, tags: HeapRef#2, meta: HeapRef#3 })
Console Stream
> [empty]

Deep Technical Breakdown

Reference Semantics & Heap Aliasing in JavaScript

  1. Stack vs Heap Storage: In V8, primitive types are allocated on the stack. Objects, arrays, and functions are allocated dynamically on the heap. When an object is assigned to a variable (const alias = original), only the memory pointer is copied. Both variables now alias the exact same heap block. Any mutation through either identifier mutates the underlying heap entity immediately.
  2. Shallow Copying Mechanics:
    • Methods like { ...original }, Object.assign({}, original), arr.slice(), and [...arr] produce shallow copies.
    • Top-level primitive values are duplicated onto the new object's shape.
    • Any property holding a reference type (nested object, array, date) has its pointer copied by value. The new object points to the same nested heap structures.
  3. Deep Cloning Alternatives:
    • structuredClone(obj): The modern ECMAScript standard for deep cloning. Handles nested structures, cyclical references, Maps, Sets, and Dates, but throws DataCloneError on functions, DOM nodes, and symbols.
    • JSON.parse(JSON.stringify(obj)): Legacy workaround. Breaks on undefined, NaN, Date (converted to string), RegExp, Map, Set, and throws on circular references.
    • Recursive custom cloning: Traverses own enumerable properties while tracking visited references in a WeakMap to support cycles.
Common Traps & Mistakes

Assuming `{ ...state }` in React or Redux deeply isolates nested data. Mutating a nested array inside a shallow-cloned state mutates previous state snapshots and bypasses change detection.

Using `Object.freeze()` expecting deep immutability. `Object.freeze()` is strictly shallow; nested properties can still be mutated.

Comparing two objects with identical keys and values using `===` and expecting `true`. Two distinct objects in memory will always yield `false` under `===`.

FAANG Follow-Up Probes
Probe #1

How does structuredClone() handle circular object references, and why does it fail on functions and DOM nodes?

Probe #2

How would you implement a recursive deepClone function in JavaScript that handles circular references using WeakMap?

Probe #3

Why does React rely on shallow equality comparisons in React.memo and useMemo, and how does shared nested mutation break memoization?