Objects, arrays and references
When you assign an object, JavaScript does not copy it. It copies a reference, so two names can point at the same order and a change through one shows up in the other. This lesson shows the mechanism, the copy tools and their limits, and the immutable update style React and Redux depend on.
By the end you can
- Explain why
b = acopies a number but shares an object - Tell mutation from reassignment, and say what
constandObject.freezereally protect - Choose between spread,
structuredCloneand an immutable update for a nested order - Reproduce and fix the "I edited the copy and the original changed" bug, with a test that catches it
The everyday picture
The storeroom at the back of a store has one door. If the store manager gives a second staff member a duplicate key, there are now two keys and still one storeroom. When one of them takes out five cartons of cooking oil, the other opens the door and finds five fewer. Nobody "copied the stock". They share it.
Compare that with photocopying the stock count sheet. Each person gets their own paper. Writing "40" on your copy changes nothing on mine. But look closely at the sheet: the last line says "detailed lines: see the blue folder on shelf 3". The photocopy says that too, and there is still only one blue folder. Both of us can open it and write in it. That is a shallow copy, the source of most bugs in this lesson.
Values and references
A variable holds one small thing. For a primitive (JavaScript in one sitting), that thing is the value itself: 1250, 'S014', true. For an object, the object lives in a separate area of memory called the heap, and the variable holds a reference to it, like an address. Assignment always copies what is in the variable. For a primitive that is the value. For an object it is the address, so you get a second key to the same room.
let a = 1250;
let b = a; // copies the value
b = 990;
console.log(a, b);
const order = { id: 'ORD-5512', totalCents: 2500 };
const sameOrder = order; // copies the reference, not the object
sameOrder.totalCents = 0;
console.log(order.totalCents);
console.log(order === sameOrder);1250 990
0
trueThe same rule applies when you call a function. The parameter receives a copy of whatever the caller passed: a copy of a number, or a copy of a reference. So a function that changes properties of an object it was given is changing the caller's object. This is sometimes called "pass by sharing".
Quick check
function markPaid(order) { order.status = 'PAID'; } is called as markPaid(myOrder). What is myOrder.status afterwards?
The function gets its own copy of the reference, and both references lead to the one order on the heap. Setting a property through either changes that object. The local name only matters for reassignment: order = {} inside the function would not affect myOrder.
b = a makes an independent copy. An object lives in the heap; order and sameOrder hold two references to the same one, so setting totalCents through either name changes the only order there is.