JavaScript / ES6+ Bootcamp – Lesson 5 – JavaScript Objects, Destructuring, Spread and this

This lesson connects several things you’ve already seen:

const [users, setUsers] = useState([]);

and:

const { name, age } = user;

and React props such as:

<User name="John" age={30} />

The goal is to understand how JavaScript objects work and how we extract, copy, and pass their data around.


1. JavaScript objects

An object is a collection of properties:

const user = {
id: 1,
name: "John",
age: 30,
active: true
};

Think of it as:

user
|
+-- id β†’ 1
+-- name β†’ "John"
+-- age β†’ 30
+-- active β†’ true

Access properties:

user.name; // "John"
user.age; // 30
user.active; // true

You can also use bracket notation:

user["name"];

These are equivalent when the property name is known.


2. Dot notation vs bracket notation

This works:

const user = {
name: "John"
};
console.log(user.name);

But bracket notation becomes particularly useful when the property name is stored in a variable:

const property = "name";
console.log(user[property]);

Result:

John

This would not work the same way:

user.property;

because that looks for a property literally called "property".


3. Objects can contain functions

For example:

const user = {
name: "John",
greet() {
console.log("Hello");
}
};

Call it:

user.greet();

The function is a property of the object.

When a function belongs to an object like this, we commonly call it a method.


4. Objects can contain anything

An object can contain:

const user = {
id: 1,
name: "John",
active: true,
roles: ["admin", "editor"],
address: {
city: "Kochi",
country: "India"
},
greet() {
console.log("Hello");
}
};

So you can have:

object
β”œβ”€β”€ primitive
β”œβ”€β”€ array
β”œβ”€β”€ object
└── function

This is why JavaScript objects are extremely flexible.


5. Object destructuring

Now the first very important ES6 feature.

Suppose:

const user = {
id: 1,
name: "John",
age: 30
};

You could write:

const name = user.name;
const age = user.age;

But destructuring allows:

const { name, age } = user;

This means:

const name = user.name;
const age = user.age;

Conceptually, yes.

So:

const { name, age } = user;

means:

Take the `name` property from user
↓
put it into variable `name`
Take the `age` property from user
↓
put it into variable `age`

6. Array destructuring vs object destructuring

This is one of the most important distinctions from the previous lessons.

Array

const numbers = [10, 20];
const [a, b] = numbers;

Destructuring is based on position:

numbers[0] β†’ a
numbers[1] β†’ b

Object

const user = {
name: "John",
age: 30
};
const { name, age } = user;

Destructuring is based on property name:

user.name β†’ name
user.age β†’ age

So:

Array destructuring β†’ position
Object destructuring β†’ property name

This distinction should become automatic.


7. Rename while destructuring

Suppose:

const user = {
name: "John",
age: 30
};

You don’t have to name your variable name.

You can do:

const { name: userName, age: userAge } = user;

Now:

userName; // "John"
userAge; // 30

Read this as:

name property β†’ variable userName
age property β†’ variable userAge

This syntax looks strange initially, but you’ll see it in real React and Node code.


8. Default values in destructuring

Suppose:

const user = {
name: "John"
};

You can write:

const { name, age = 30 } = user;

Now:

name; // "John"
age; // 30

The default is used only when the property is undefined.


9. Nested destructuring

Given:

const user = {
name: "John",
address: {
city: "Kochi",
country: "India"
}
};

You can do:

const {
name,
address: { city, country }
} = user;

Now:

name; // "John"
city; // "Kochi"
country; // "India"

This is useful, but don’t overuse deep destructuring. Very complex destructuring can become harder to read than ordinary property access.


10. Function parameters can be destructured

This is very common in React.

Instead of:

function User(props) {
return <h2>{props.name}</h2>;
}

you can write:

function User({ name }) {
return <h2>{name}</h2>;
}

The second form says:

The function receives an object, and I want its name property.

Suppose React effectively gives:

{
name: "John",
age: 30
}

Then:

function User({ name }) {

extracts:

name = "John";

This is one of the reasons you see React code full of { ... } around function parameters.


11. useState vs props: two different destructuring styles

Now compare these:

const [users, setUsers] = useState([]);

and:

function User({ name, age }) {
}

The first is array destructuring:

position 0 β†’ users
position 1 β†’ setUsers

The second is object destructuring:

name property β†’ name
age property β†’ age

This is a very important React distinction.


12. Spread syntax with objects

Let’s revisit spread:

const user = {
name: "John",
age: 30
};
const copy = {
...user
};

Now:

copy

contains:

{
name: "John",
age: 30
}

The spread operator copies the object’s own enumerable properties into a new object.

At a beginner level, think:

“Take the properties from this object and put them into a new object.”


13. Updating an object using spread

Suppose:

const user = {
name: "John",
age: 30
};

We want:

same user
but age = 31

Write:

const updatedUser = {
...user,
age: 31
};

Result:

{
name: "John",
age: 31
}

Why does age: 31 win?

Object properties appearing later overwrite earlier properties with the same key.

So conceptually:

...user
↓
name: John
age: 30
age: 31
↓
overwrite age

14. This is common in React state updates

Suppose:

const [user, setUser] = useState({
name: "John",
age: 30
});

To change just the age:

setUser({
...user,
age: 31
});

We don’t do:

user.age = 31;

for normal React state updates.

Instead, we create a new object.

Conceptually:

old object
↓
{ name: "John", age: 30 }
...copy...
new object
↓
{ name: "John", age: 31 }

This gives React a new object reference.

We’ll later connect this directly to the question you raised previously about why React cares about new references.


15. Spread with arrays

Given:

const users = ["John", "Jane"];

Append without modifying the original:

const newUsers = [...users, "Mike"];

Result:

["John", "Jane", "Mike"]

Prepend:

const newUsers = ["Mike", ...users];

Result:

["Mike", "John", "Jane"]

Merge:

const first = [1, 2];
const second = [3, 4];
const combined = [...first, ...second];

Result:

[1, 2, 3, 4]

16. Spread is shallow

This is an important interview topic.

Consider:

const user = {
name: "John",
address: {
city: "Kochi"
}
};
const copy = {
...user
};

copy is a new object.

But:

copy.address === user.address

is:

true

Why?

Because spread creates a shallow copy.

The outer object is new, but nested objects are still shared.

Visualize:

user ────────────────┐
β”‚
copy ─────────────────
↓
address
{ city: "Kochi" }

Both objects point to the same nested address object.

This becomes extremely important when we talk about immutable state.


17. Deep copy is a different problem

Don’t assume:

const copy = { ...user };

means:

“Everything inside user has been recursively copied.”

It hasn’t.

For modern applications there are several strategies for deep cloning, but don’t jump to one automatically. Often you don’t actually need a deep clone.

For React state, it’s usually better to create new references only along the part of the structure you’re changing.

For example:

const updatedUser = {
...user,
address: {
...user.address,
city: "Bengaluru"
}
};

Now both the outer object and changed nested object are new references.


18. Object shorthand

ES6 gives us convenient syntax here:

const name = "John";
const age = 30;
const user = {
name,
age
};

Instead of:

const user = {
name: name,
age: age
};

JavaScript understands that:

name

means:

name: name

when used in an object literal.

You’ll see this everywhere.


19. Computed property names

You can dynamically create a property name:

const field = "name";
const user = {
[field]: "John"
};

Result:

{
name: "John"
}

Another example:

const field = "email";
const value = "john@example.com";
const user = {
[field]: value
};

Result:

{
email: "john@example.com"
}

This is useful when dynamically building objects.


20. this – the important part

Now we reach one of JavaScript’s most confusing concepts for Ruby developers:

this

Consider:

const user = {
name: "John",
greet() {
console.log(this.name);
}
};
user.greet();

Output:

John

Here:

this

refers to the object used to call the method:

user.greet();

So conceptually:

user.greet()
↓
this = user

Therefore:

this.name

is:

user.name

21. A critical difference from Ruby

In Ruby, you may mentally think:

“self is the current object.”

JavaScript’s this is more nuanced.

One of the most useful rules:

For a normal function call, this is determined by how the function is called, not simply where the function was defined.

That means:

user.greet();

and:

const greet = user.greet;
greet();

can have different this behavior.

This is one of the reasons JavaScript’s this can be tricky.


22. Arrow functions and this

Arrow functions behave differently.

They do not create their own this.

Consider:

const user = {
name: "John",
greet: () => {
console.log(this.name);
}
};
user.greet();

Do not assume:

this = user

just because the function appears inside the object.

That is not how arrow functions work.

This is one of the reasons you should not blindly replace every regular function with an arrow function.

We’ll spend an entire lesson on this, call, apply, bind, and arrow-function behavior.


23. Object references

Now an important JavaScript behavior.

const user1 = {
name: "John"
};
const user2 = user1;

We did not create a new object.

Both variables refer to the same object:

user1 ─────┐
↓
{ name: "John" }
↑
user2 β”€β”€β”€β”€β”€β”˜

Therefore:

user2.name = "Jane";
console.log(user1.name);

prints:

Jane

This is because objects are reference values.


24. Compare primitive values

Now compare:

let a = 10;
let b = a;
b = 20;
console.log(a);

Result:

10

For the primitive number:

a β†’ 10
b β†’ 10
then:
b β†’ 20

Changing b doesn’t alter a.

With objects:

user1 ──┐
↓
object
↑
user2 β”€β”€β”˜

Both references point to the same object.

This distinction will become crucial when we discuss:

  • React state
  • equality
  • immutability
  • shallow comparison
  • useMemo
  • useCallback

25. === with objects

This gives us another important interview question.

const user1 = { name: "John" };
const user2 = { name: "John" };
console.log(user1 === user2);

Result:

false

Why?

Because these are two different objects.

Even though their contents are identical:

user1 β†’ Object A
user2 β†’ Object B

They have different references.

But:

const user1 = { name: "John" };
const user2 = user1;
user1 === user2;

returns:

true

because both reference the same object.


26. This explains something important in React

Later you’ll see code like:

setUsers(users);

versus:

setUsers([...users]);

The first gives React the same array reference.

The second creates a new array:

users ───────→ old array
[...users] ──→ new array

This distinction becomes important because React can use reference identity when determining whether state/props values changed.

We’ll go deep into this when we revisit React state and rendering.


27. Complete React example

Let’s put today’s concepts together.

function User({ user }) {
const { name, age } = user;
return (
<div>
<h2>{name}</h2>
<p>{age}</p>
</div>
);
}

Here we have:

Object parameter

{ user }

Object destructuring

const { name, age } = user;

Function

function User(...) {
}

And React passes an object as the argument.


28. Another React example

Suppose:

const [user, setUser] = useState({
name: "John",
age: 30,
city: "Kochi"
});

Update only the city:

setUser({
...user,
city: "Bengaluru"
});

This combines:

object
+
spread
+
new object reference
+
state update

And now you can see why learning JavaScript first is useful.


🧠 The five concepts to remember

At the end of this lesson, these should be clear:

1. Object destructuring

const { name, age } = user;

Means:

const name = user.name;
const age = user.age;

2. Array destructuring

const [first, second] = numbers;

Means:

const first = numbers[0];
const second = numbers[1];

3. Spread

const copy = { ...user };

Creates a new shallow object.

4. References

const b = a;

with objects means both variables refer to the same object.

5. this

For a normal method call:

user.greet();

this inside the method refers to the receiver of that call – user.

Arrow functions have different this behavior.


🎯 Int. questions

Try answering these before reading further.

Q1

What’s the difference?

const [a, b] = [10, 20];

and:

const { a, b } = { a: 10, b: 20 };

Q2

What does this produce?

const user = {
name: "John",
age: 30
};
const { name: userName } = user;

Q3

What is the difference between:

const user2 = user1;

and:

const user2 = { ...user1 };

Q4

What happens here?

const user1 = { name: "John" };
const user2 = { name: "John" };
console.log(user1 === user2);

Q5

What does this do?

const user = {
name: "John",
age: 30
};
const updated = {
...user,
age: 31
};

Q6

Why can this be dangerous to assume?

const copy = { ...user };

when user contains nested objects?


πŸ§ͺ Live coding practice

Problem 1 – Destructure

Given:

const user = {
id: 10,
name: "John",
age: 30
};

Extract:

id
name
age

using object destructuring.


Problem 2 – Rename

Extract name into a variable called:

userName

Problem 3 – Update immutably

Given:

const user = {
name: "John",
age: 30
};

Create a new object where:

name = "Jane"

without modifying user.


Problem 4 – Add a property

Given:

const user = {
name: "John"
};

Create:

{
name: "John",
active: true
}

using spread.


Problem 5 – Nested object

Given:

const user = {
name: "John",
address: {
city: "Kochi",
country: "India"
}
};

Create a new object where only:

city = "Bengaluru"

changes.

Don’t mutate the original nested address.


Problem 6 – Reference test

Predict:

const a = { count: 1 };
const b = a;
b.count = 2;
console.log(a.count);

Then compare:

const a = { count: 1 };
const b = { ...a };
b.count = 2;
console.log(a.count);

Explain why the outputs differ.


πŸ”— How this connects to your React learning

You now have almost all the JavaScript pieces needed to understand this:

const [users, setUsers] = useState([]);

and:

function User({ name, age }) {

and:

setUser({
...user,
age: 31
});

They are no longer “React syntax”. They are combinations of ordinary JavaScript:

useState(...) β†’ function call
[users, setUsers] β†’ array destructuring
{ name, age } β†’ object destructuring
...user β†’ spread
user => ... β†’ function / callback

That’s exactly the mental model we want.

Next lesson

Lesson 6 – this, call, apply, bind, arrow functions, and closures.

This is the first lesson where JavaScript will behave noticeably differently from Ruby, and it is especially important for understanding React event handlers and Node.js code.

Unknown's avatar

Author: Abhilash

Hi, I’m Abhilash! A seasoned web developer with 15 years of experience specializing in Ruby and Ruby on Rails. Since 2010, I’ve built scalable, robust web applications and worked with frameworks like Angular, Sinatra, Laravel, Node.js, Vue and React. Passionate about clean, maintainable code and continuous learning, I share insights, tutorials, and experiences here. Let’s explore the ever-evolving world of web development together!

Leave a comment