Skip to content

Instantly share code, notes, and snippets.

@yehezkieldio
Created July 20, 2025 07:21
Show Gist options
  • Save yehezkieldio/f0b46e04a196dbb9b34b683b47dcc55d to your computer and use it in GitHub Desktop.
Save yehezkieldio/f0b46e04a196dbb9b34b683b47dcc55d to your computer and use it in GitHub Desktop.

Key Takeaway: JS/TS is ALWAYS Pass-by-Value. The "value" being passed is either a primitive directly, or a reference (a pointer) to an object.


1. Primitives (Stack-like Behavior, Value Copied)

  • Types: number, string, boolean, bigint, symbol, null, undefined
  • What's Passed: An exact copy of the value itself.
  • Behavior:
    • When you pass a primitive to a function, the function gets its own, independent copy.
    • Re-assigning the parameter inside the function does not affect the original variable outside.
  • Rust Analogy: Very similar to passing a Copy type (e.g., u32, bool) by value in Rust.

Example:

function modifyPrimitive(x: number) {
  x = 42; // This 'x' is a local copy.
  console.log(`Inside: x = ${x}`); // 42
}

let num = 10;
console.log(`Before: num = ${num}`); // 10
modifyPrimitive(num);
console.log(`After: num = ${num}`); // 10 (Original 'num' is unchanged)

2. Objects (Heap Behavior, Reference Copied)

  • Types: object, array, function, Date, Map, Set, class instances, RegExp, Promise, Error, etc.
  • What's Passed: A copy of the reference (a pointer/memory address) to the object on the heap.
  • Behavior:
    • Both the original variable and the function's parameter now point to the same object in memory.
    • Mutating properties of the object through the function parameter IS visible to the original variable. (Think &mut T in Rust, where you can modify the data through the reference).
    • Re-assigning the parameter variable itself to a new object is NOT visible to the original variable. This just makes the local parameter point to a different object, leaving the original variable untouched. (Think let mut x = ...; x = new_value; for a local variable, it doesn't affect an outside binding).
  • Rust Analogy:
    • Passing an object reference is like passing &T (for reading) or &mut T (for modifying properties) in Rust. You're working with the same underlying data.
    • There's no explicit ownership transfer like in Rust; multiple references can point to the same object simultaneously. Memory management (garbage collection) handles deallocation when no references remain.

Examples:

// --- Case A: Mutating the object's properties (Visible outside) ---
function modifyArrayInPlace(arr: number[]) {
  arr.push(99); // Mutates the *original* array pointed to by 'arr'
  arr[0] = 100; // Also visible outside
  console.log(`Inside (mutation): arr =`, arr); // [100, 2, 99]
}

const myArray = [1, 2];
console.log(`Before (mutation): myArray =`, myArray); // [1, 2]
modifyArrayInPlace(myArray);
console.log(`After (mutation): myArray =`, myArray); // [100, 2, 99] (Changed!)

// --- Case B: Re-assigning the parameter itself (NOT visible outside) ---
function reassignParameter(obj: { key: string }) {
  obj = { key: "new object" }; // 'obj' now points to a *new*, different object.
  console.log(`Inside (re-assignment): obj =`, obj); // { key: 'new object' }
}

let myObject = { key: "original object" };
console.log(`Before (re-assignment): myObject =`, myObject); // { key: 'original object' }
reassignParameter(myObject);
console.log(`After (re-assignment): myObject =`, myObject); // { key: 'original object' } (Unchanged!)

Cloning in JS/TS

  • No Implicit Deep Cloning: Unlike Rust's Clone trait which can be implemented for deep copies, JS/TS functions generally create shallow copies by default.
  • Shallow Copy: Copies the top-level structure, but nested objects are still references to the original nested objects.
    • [...originalArray]
    • { ...originalObject }
    • Object.assign({}, originalObject)
  • Deep Copy:
    • structuredClone(obj): The modern, preferred built-in way (available in most modern environments). Handles many complex types but not functions or DOM nodes.
    • JSON.parse(JSON.stringify(obj)): A hacky method, with significant limitations (loses functions, undefined, turns Date objects into strings, etc.).
    • Third-party libraries (e.g., Lodash's _.cloneDeep): Offer more robust deep cloning solutions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment