Think of a variable like a name tag stuck to a box. The box can hold a number, a word, or even a bigger object, and the name tag tells you which box to look at.
Some boxes are tiny and contain the actual thing directly, like 5 or 'hi'. Other boxes point to a shared storage area, like a file cabinet, where the real object lives.
That difference matters when you copy or compare them. Sometimes you copy the thing itself, and sometimes you only copy the address to where the thing lives.
What Variables and Data Types Are
JavaScript has primitive values and reference values. Primitives are simple, immutable values like number, string, boolean, null, undefined, symbol, and bigint. Reference types are objects and arrays, where the variable holds a reference to the value rather than the value itself.
This matters because it changes how assignment, mutation, and equality behave. It also explains why let, const, and var differ: they control binding rules, not whether a value is mutable.
Interviewers often use this topic to test whether you understand storage intuition, copying semantics, and the difference between ==, ===, and Object.is.
`let`, `const`, and `var`
let and const are block-scoped. let lets you reassign the variable, while const prevents reassignment of the binding.
var is function-scoped, can be redeclared, and is hoisted in a way that often confuses people. It also creates a lot of interview traps because its scope is less predictable than let/const.
A key nuance: const does not make an object immutable. It only prevents the variable from being pointed at a different value.
const user = { name: 'Ada' };
user.name = 'Grace'; // OK
// user = {}; // TypeError
let count = 1;
count = 2; // OK
var legacy = 10;
legacy = 11;
Primitives vs Reference Types
A primitive is copied by value. If you assign one primitive variable to another, you get an independent copy.
A reference type is copied by reference. If two variables point to the same object, mutating through one variable affects what the other sees.
The common intuition is stack vs heap: the variable binding lives in a fast local slot, while object contents live elsewhere. That model is useful, but the practical takeaway is simpler: primitives copy cleanly, objects share identity.
let a = 5;
let b = a;
b = 9;
console.log(a); // 5
let x = { n: 1 };
let y = x;
y.n = 2;
console.log(x.n); // 2
Copying and Mutation
When you copy an object, you usually copy only the reference, not the full object. That means two variables can still point to the same underlying data.
Mutation changes the existing object in place, so every reference sees the change. Reassignment, on the other hand, only changes which value the variable name points to.
This distinction is a favorite interview test: obj = {} is not the same as obj.name = 'x'. The first changes the binding, the second mutates the object.
Type coercion is JavaScript converting a value from one type to another, often implicitly. It happens most often with ==, arithmetic, and boolean contexts.
== allows coercion before comparing, while === compares both type and value with no coercion. In interviews, == is usually where the traps live.
Object.is is like a stricter equality check for edge cases. It treats NaN as equal to itself and distinguishes +0 from -0, which === does not.
Suppose you see this code and are asked what it prints:
const a = { n: 1 }
const b = a
b.n = 2
console.log(a.n, b.n)
The answer is 2 2 because a and b point to the same object, so mutating through b changes the shared object.
Now compare a primitive version:
let x = 1
let y = x
y = 2
console.log(x, y)
That prints 1 2 because primitives are copied by value, so x is unaffected.
const a = { n: 1 };
const b = a;
b.n = 2;
console.log(a.n, b.n); // 2 2
let x = 1;
let y = x;
y = 2;
console.log(x, y); // 1 2
Classic Comparison Traps
A common trap is assuming == is harmless because it “just compares values.” In reality, it may convert types first, which can make surprising expressions true.
Examples interviewers like:
'' == 0 → true
false == 0 → true
null == undefined → true
[] == false → true
The safest rule is usually to use === unless you explicitly need coercion. If you care about exact identity edge cases like NaN or signed zero, use Object.is.
Thinking const makes an object immutable. It only freezes the binding, not the contents.
Assuming objects are copied by value when assigned. Assignment copies the reference.
Confusing mutation with reassignment.
Using == without knowing the coercion rules.
Forgetting that typeof null is 'object', which is a long-standing JavaScript bug.
Assuming NaN === NaN is true; it is not.
The safest interview answer is to explain both the behavior and the reason behind it. If you can mention primitive copy-by-value, reference sharing, and coercion explicitly, you’ll sound grounded rather than memorized.