# How the JavaScript Spread Operator Works with Objects and Arrays

> Discover how the JavaScript spread operator works with objects and arrays. Learn to create shallow copies efficiently and expand iterables for cleaner code.

- Repository: [Lydia Hallie/javascript-questions](https://github.com/lydiahallie/javascript-questions)
- Tags: deep-dive
- Published: 2026-03-04

---

**The JavaScript spread operator (`...`) creates shallow copies by expanding the elements of iterables (arrays, strings) or the own enumerable properties of objects into a new container.**

The `lydiahallie/javascript-questions` repository documents this ES6 feature extensively across its educational documentation. Mastering how the **spread operator** handles different data structures prevents common mutation bugs and enables concise data transformations.

## Spreading Iterables: Arrays and Strings

When applied to **iterables**, the spread operator invokes the object's `[Symbol.iterator]` method and expands each yielded value into the surrounding context. According to the repository's README (see line 1335), this works with any object implementing the iterable protocol.

### Cloning and Concatenating Arrays

The most common use case involves **array manipulation** without mutating the original:

```javascript
// Clone an array
const original = [1, 2, 3];
const clone = [...original]; // [1, 2, 3]

// Concatenate multiple arrays
const evens = [2, 4, 6];
const odds = [1, 3, 5];
const combined = [...evens, ...odds]; // [2, 4, 6, 1, 3, 5]

```

### Converting Strings to Character Arrays

Because strings are iterables, spreading them splits the sequence into individual characters:

```javascript
const chars = [...'JavaScript']; 
// ['J', 'a', 'v', 'a', 'S', 'c', 'r', 'i', 'p', 't']

```

## Spreading Objects and Shallow Copying

When the operand is an object, the operator performs an internal `Object.assign({}, source)`‑like operation, copying only **own enumerable properties** to the new object. The repository illustrates this behavior at line 1896 with practical merging examples.

### Merging Objects Without Mutation

```javascript
const defaults = { enabled: false, level: 1, mode: 'standard' };
const overrides = { enabled: true, mode: 'admin' };

const config = { ...defaults, ...overrides };
// { enabled: true, level: 1, mode: 'admin' }

```

This pattern creates a new object containing all properties from both sources without modifying either original.

### Overriding Specific Properties

You can combine spreading with explicit property definitions to set defaults and overrides in a single expression:

```javascript
const user = { name: "Lydia", age: 21 };
const updated = { ...user, age: 22 }; 
// { name: "Lydia", age: 22 }

```

## Destructuring and Default Parameters

The spread operator pairs naturally with **destructuring** to handle function parameters with defaults. The repository demonstrates this pattern around line 2026.

```javascript
function createUser({ name = "Anonymous", role = "user", ...rest } = {}) {
  return { name, role, ...rest };
}

createUser({ age: 30 }); 
// { name: "Anonymous", role: "user", age: 30 }

```

This approach extracts specific fields while collecting remaining properties into a rest object, ensuring the function works even when called with no arguments.

## Under the Hood: Iterator Protocols and Enumeration

Understanding the internal mechanics clarifies why certain edge cases behave unexpectedly:

- **Arrays and Strings**: JavaScript calls the operand's `[Symbol.iterator]` method, sequentially inserting each returned value at the spread location.
- **Objects**: The engine enumerates only **own enumerable** keys from the source object, excluding prototype chain properties and non-enumerable symbols.

## Critical Caveats When Using Spread

Despite its convenience, the operator introduces specific limitations that affect data integrity:

- **Shallow Copies Only**: Nested objects and arrays maintain reference equality with the source. Mutating a nested array in the clone affects the original.
- **Non-Enumerable Exclusion**: Properties defined with `Object.defineProperty()` as non-enumerable do not transfer.
- **Last Value Wins**: When spreading multiple objects with identical keys, the rightmost object's value overwrites previous ones.
- **Iterator Order**: For non-array iterables like `Map` or `Set`, the resulting order depends on the iterator's implementation.

```javascript
// Demonstrating shallow copy limitation
const original = { nested: [1, 2] };
const copy = { ...original };

copy.nested.push(3);
console.log(original.nested); // [1, 2, 3] - mutated!

```

## Summary

- The **spread operator** creates shallow copies of arrays, strings, and objects by expanding their contents into new containers.
- It invokes **iterator protocols** for iterables and enumerates **own properties** for objects, as documented in `lydiahallie/javascript-questions` lines 1335 and 1896.
- Use it for **immutable updates** by combining multiple sources into new arrays or objects without mutating originals.
- It supports **default parameter patterns** when destructuring function arguments, enabling robust API designs.
- Remember it performs **shallow cloning** only; nested structures remain shared references between source and copy.

## Frequently Asked Questions

### Does the spread operator create a deep copy?

No. The spread operator performs **shallow cloning** only. It copies top-level elements or properties by value (or by reference for objects), but nested arrays and objects remain linked to the original source. For deep cloning, you must use libraries like `lodash.cloneDeep` or structured cloning algorithms.

### Can you spread non-iterable objects into arrays?

No. Attempting to spread a plain object into an array throws a TypeError because the object lacks a `[Symbol.iterator]` method. Only objects implementing the iterable protocol—such as Arrays, Strings, Sets, Maps, and TypedArrays—work with array spreading. Object spreading requires object literal syntax `{...obj}`.

### Why do later properties overwrite earlier ones when spreading objects?

JavaScript processes the spread operator sequentially from left to right. When multiple objects contain the same key, the **last definition wins** because each subsequent spread overwrites the previous value for that property. This behavior mimics `Object.assign()`, making it predictable for configuration merging where defaults precede overrides.

### How does spreading differ from Object.assign?

Both create shallow copies of own enumerable properties, but the **spread operator** cannot trigger setters and creates a new object literal syntax that is easier to read for simple merges. `Object.assign()` mutates its first argument (the target), while spreading always creates a new object. Additionally, spread syntax handles symbols more consistently across different JavaScript engines.