DEV Community

A Kast
A Kast

Posted on

Objects vs Primitives: JS Interview Cheatsheet  with Traps, and Boxing Explained

“Why can you call .toUpperCase() on a string, but can't add a custom property to it?”

This question is used in interviews to check your understanding of primitives and objects in JavaScript.

In this article, we break down 3 differences that will save you from hidden bugs:

  • Storage, copying, and comparison — why two identical objects are never equal, and how passing by reference can betray you
  • Mutability — how to accidentally overload your process with primitive copies (and when it actually matters)
  • Methods and autoboxingwhy primitives pretend to have methods, and why null and undefined refuse to play along

Everything you need for confident answers in interviews and clean code on the job.


Want to test your knowledge right away?
👉 Free mock interview based on this article- AI will ask you questions, check your answers, and point out gaps in your understanding of this topic. 5–10 minutes - and you'll know what you've mastered and what still needs work.


Fundamental Concepts

Mutability, storage by value and by reference, comparison — these are basic programming concepts. In this section, we simply clarify how these concepts work in JS, without explaining them from scratch.


1. All Data in JS Falls into Primitives and Objects

In JavaScript, all data types fall into two categories: primitives and objects. This division determines how data is stored in memory, copied, and behaves in code.

Question What two categories can JS types be divided into?
Answer Elementary types (primitives) and object types

Primitives (7 types)

Type Description
Number Integers and floats, including NaN and Infinity
String Textual data
Boolean Logical values: true and false
null Intentional absence of value
undefined Value not assigned
Symbol Unique identifiers (ES6+)
BigInt Integers with arbitrary precision (ES2020+)
Question What primitive types exist in JS?
Answer Number, String, Boolean, null, undefined, Symbol (ES6+), BigInt

Objects (Examples, Not a Full List)

Type Description
Object Basic objects { key: value }
Array Ordered collections of data
Function Callable objects
Date Working with dates and times
RegExp Regular expressions
Map / Set Collections with special behavior (ES6+)
Error Error objects

Summary

JavaScript has 7 primitives and many types of objects. Primitives are simple values; objects are complex structures.


2. How Copying and Comparison Work: Objects by Reference, Primitives by Value

Primitives — by Value

When assigning or passing to a function, primitives are copied entirely. Each variable gets its own independent copy.

Example (how it works):

let a = 5;
let b = a;
a = 10;
console.log(b); // 5
Enter fullscreen mode Exit fullscreen mode

Here, b gets a copy of a's value. When we change a to 10, b remains 5 — it's an independent copy, unrelated to the original.

Example (what won’t happen):

function change(x) {
  x = 100;
}

let value = 5;
change(value);
console.log(value); // 5
Enter fullscreen mode Exit fullscreen mode

Many developers, especially those coming from other languages, expect the function to change the passed number. But primitives are always passed by value, so value remains 5. Inside the function, it only works with a local copy.

If you need a function to change a primitive, you'll have to return the new value and assign it outside. Primitives can't be mutated, which makes code more predictable but sometimes requires extra steps.

Objects — by Reference

When assigning or passing to a function, objects copy a reference to the same data. Variables point to the same object in memory.

Example (how it works):

let a = { value: 5 };
let b = a; // reference is copied
a.value = 10;
console.log(b.value); // 10
Enter fullscreen mode Exit fullscreen mode

b doesn't get a copy of the object, but a reference to the same object in memory. When we change a.value, the change is visible in b because both variables point to the same data.

Example (unexpected behavior):

function change(obj) {
  obj.value = 100;
}

let data = { value: 5 };
change(data);
console.log(data.value); // 100
Enter fullscreen mode Exit fullscreen mode

A developer might expect the function to work with a copy and not affect the original. But objects are passed by reference, so changes inside the function directly mutate the original object. This is a common source of unexpected bugs.

Example (🚩 Interview Trap):

function updateObject(obj) {
  obj = { value: 100 }; // Creating a NEW object
}

let data = { value: 5 };
updateObject(data);
console.log(data.value); // 5 - NOT 100!
Enter fullscreen mode Exit fullscreen mode

obj = { value: 100 } overwrites the reference inside the function, but doesn't change the original object. The parameter obj is a local variable holding a copy of the reference. When we assign a new object to it, we break the connection with the original data.

On one hand, passing by reference is efficient — no need to copy large objects. On the other hand, it can lead to hard-to-find side effects when a function unexpectedly changes data that was supposed to remain unchanged.


Question How are objects compared in JavaScript?
Answer Objects (including arrays) are compared by reference, not by content. Two separate objects (different references) are not equal, even if they have the same properties and values. Two separate arrays are not equal, even if they contain the same elements in the same order.
Question How does assigning an object or array work in JavaScript?
Answer When assigning an object or array, a reference to the existing object/array is created. A new object isn't created — both variables point to the same memory location. Changes through one variable will be visible through the other.

Interview question: What will be the result of this code and why?

let a = [];
let b = a;
b[0] = 1;
console.log(a[0]);
console.log(a == b);
Enter fullscreen mode Exit fullscreen mode

Answer: a[0] → 1, a == b → true

Explanation: let a = [] creates a new array in memory. let b = a copies the reference to the array, not the array itself. When we change b[0] = 1, we modify the original array. Since a and b refer to the same object, the changes are visible through both variables, and a == b returns true.


Interview question: What will be the result of this code and why?

let obj1 = {x: 1}, obj2 = {x: 1};
console.log(obj1 == obj2);

let arr1 = [], arr2 = [];
console.log(arr1 == arr2);
Enter fullscreen mode Exit fullscreen mode

Answer: obj1 == obj2 → false, arr1 == arr2 → false

Explanation: In JavaScript, objects and arrays are compared by reference, not by content. When creating objects/arrays with the same content, different instances are created. The == operator checks whether the variables refer to the same object in memory. Even if properties/elements are identical, these are different objects with different references.


Question Which data types in JavaScript are reference types?
Answer Reference types are objects (including arrays, functions, dates, etc.)

Summary

  • Primitives — copied independently and compared by value
  • Objects — copy the reference and are compared by reference
  • Functions can unexpectedly mutate passed objects but cannot change primitives

3. Mutability: Objects Are Mutable, Primitives Are Immutable

Primitives — Immutable

The value of a primitive cannot be changed. Any operation creates a new value, while the old one stays in memory until garbage collection.

Example (how it works):

let str = 'hello';
let upper = str.toUpperCase();

console.log(str);   // 'hello' - original unchanged
console.log(upper); // 'HELLO' - new value
Enter fullscreen mode Exit fullscreen mode

.toUpperCase() doesn't change the original string; it returns a new string with the result. The original str remains unchanged. This demonstrates a key property of primitives — immutability.

Example (what doesn't work):

let str = 'hello';
str[0] = 'H'; // trying to change the first character
console.log(str); // 'hello' — unchanged

let num = 5;
num.someProp = true; // trying to add a property
console.log(num.someProp); // undefined - property wasn't added
Enter fullscreen mode Exit fullscreen mode

Index access in strings is read-only. Trying to change a character is ignored (in non-strict mode) or throws an error (in strict mode). Similarly, trying to add a property to a number doesn't throw an error, but the property disappears because primitives can't store extra data.

Immutability makes code predictable — you always know that passing a primitive to a function won't change the original. However, it also means that for every "change," a new value must be created, which can put a strain on memory and the garbage collector when working with large amounts of data (e.g., very long strings).

Example (🚩 Memory Load):

// Imagine you're parsing a huge log file (hundreds of thousands of lines)
let logData = "2024-01-15 10:23:45 [ERROR] Database connection failed...";
// and so on for a million lines

// Naive processing:
let processed = logData.toLowerCase();   // creates a copy of the entire log (1 million lines)
let cleaned = processed.replace(/\d+/g, ''); // creates another copy
let lines = cleaned.split('\n');         // creates an array and new strings

// At this point, multiple copies of the same text are stored in memory
// Original + toLowerCase + replace + array of strings = 3–4x memory load
Enter fullscreen mode Exit fullscreen mode

How likely is this in practice?

Scenario Likelihood Explanation
Regular website (forms, lists, API requests) Almost never Strings rarely exceed a few kilobytes
Chat / messenger (message history) Rare Only when loading large history, browser handles it
Browser-based editor (Google Docs, Notion) Possible When working with large documents
Server-side (Node.js) log processing Possible If logs are large and processed in memory

In everyday frontend development, you're unlikely to encounter this problem. But if you're writing a text editor, working with large files in Node.js, or processing logs in an admin panel — it's worth remembering that each string operation creates a new copy. In such cases, it's more efficient to use streams or work with byte arrays.

Objects — Mutable

Objects can be changed at any time: properties can be added, modified, or deleted. All references to the object see these changes.

Example (how it works):

let obj = { value: 10 };
obj.value = 20;       // changing an existing property
obj.newProp = 'hello'; // adding a new property
console.log(obj);     // { value: 20, newProp: 'hello' }
Enter fullscreen mode Exit fullscreen mode

Example (side effect):

let obj = { value: 10 };
let obj2 = obj;
obj2.value = 20;
console.log(obj.value); // 20 — change through obj2 affects obj

delete obj.value;
console.log(obj2);      // { newProp: 'hello' } - deletion through obj is visible in obj2
Enter fullscreen mode Exit fullscreen mode

A developer might think obj2 is an independent copy. But in reality, obj2 holds a reference to the same object in memory. Therefore, any change through obj or obj2 is reflected in all variables referencing that object. This is a common source of unexpected bugs.

Mutability offers flexibility: you can work with an object from different parts of the program, and everyone sees the current data. But it can also be a source of hard-to-find bugs, especially when an object is passed to functions and changed unexpectedly for the calling code. In large applications, this requires discipline and sometimes special approaches (immutability, copying objects).

Question Are types in JS mutable or not?
Answer Mutable types: object types. Immutable types: elementary types (primitives).

Summary

  • Primitives cannot be changed — you can only create a new value.
  • Objects can be freely changed, but this can lead to side effects.
  • Main rule: primitives are immutable, objects are mutable.

4. Methods: Objects Have Them, Primitives Get Them Through Boxing

Formally, only objects have methods. But the primitives string, number, boolean, and symbol behave as if they have methods thanks to a mechanism called boxing.

Example:

let str = 'hello';
console.log(str.toUpperCase()); // 'HELLO' — it works!
Enter fullscreen mode Exit fullscreen mode

Example (what happens under the hood):

// When you write str.toUpperCase(), JS does this:
let temp = new String('hello'); // temporary object is created
let result = temp.toUpperCase(); // method is called
temp = null; // object is destroyed

// Attempting to add a property to a primitive
let str = 'hello';
str.customProp = 'word';
console.log(str.customProp); // undefined - property was added to a temporary object and disappeared
Enter fullscreen mode Exit fullscreen mode

How It Looks in Memory:

Step 1: let str = 'hello'

  • A primitive "hello" is created in memory
  • The variable str points to it

Step 2: str.toUpperCase()

  • A temporary wrapper object new String("hello") is created
  • The .toUpperCase() method is called on this object
  • The primitive str continues to exist separately

Step 3: After the call

  • The temporary object is destroyed
  • The primitive str remains unchanged
  • The method's result is returned as a new primitive

Autoboxing creates the illusion that primitives have methods. It's usually convenient, but it's important to remember: trying to assign a property to a primitive will "disappear" because the property is added to a temporary object that is immediately destroyed.

Question Which types have methods in JS?
Answer Formally, only JavaScript objects have methods. However, the following elementary types behave as if they have methods: string, number, boolean, symbol (thanks to boxing).

Exception: null and undefined

These two primitives don't even have the illusion of methods.

Example:

null.length;      // TypeError: Cannot read properties of null
undefined.haha(); // TypeError: Cannot read properties of undefined
Enter fullscreen mode Exit fullscreen mode

This makes them convenient for existence checks — if a value is null or undefined, you can't accidentally call a method on it and get an unexpected result.

Question In JS, which values can't have methods called on them?
Answer null and undefined.

Summary

  • Primitives get methods through temporary wrappers.
  • Exception — null and undefined, methods cannot be called on them.

You've studied the theory, now it's time to solidify it.

It seems clear — until you try to answer questions.

👉 Free mock interview based on this article — AI will ask you questions, check your answers, and point out gaps. 5–10 minutes — and you'll know exactly what you've mastered and what still needs work.


You can also find this and other articles on my Medium:

Top comments (0)