π΄ JavaScript Rest and Spread Operators
JavaScript introduced the Rest Parameter and Spread Operator to make working with functions, arrays, and objects easier.
Although both use the same syntax (...), they have different purposes:
Rest Parameter (
...) β Collects multiple values into an array or object.Spread Operator (
...) β Expands an array or object into individual elements.
Remember:
Rest = Collect π₯
Spread = Expand π€
π§± Rest Parameter
The Rest Parameter collects multiple arguments into a single array.
π Example
Imagine you're hosting a party, but you don't know how many guests will arrive.
function welcomeGuests(...guests) {
console.log("Welcome:", guests);
}
welcomeGuests("Alice", "Bob", "Charlie"); // Welcome: ["Alice", "Bob", "Charlie"]π Explanation
...guestscollects all the arguments into an array calledguests.
πͺ Use Cases of Rest Parameter
function greet(greeting, ...names) {
return `${greeting}, ${names.join(" and ")}`;
}
console.log(greet("Hello", "Pranjal", "Tanamaya", "Sonam"));
// Hello, Pranjal and Tanamaya and Sonamπ Explanation
greetingstores"Hello"....namescollects the remaining arguments into an array.join(" and ")joins the names into one string.
${}is used inside template literals to insert variables or expressions.
const { a, ...rest } = { a: 1, b: 2, c: 3 };
console.log(a); // 1
console.log(rest); // { b: 2, c: 3 }π Explanation
aextracts the value of propertya....restcollects the remaining properties into a new object.
Destructuring means unpacking values from an object or array into separate variables.
const [first, ...rest] = [1, 2, 3, 4];
console.log(first); // 1
console.log(rest); // [2, 3, 4]π Explanation
firststores the first element....reststores the remaining elements in a new array.
<div class="infographic-restonly">
<style>
.infographic-restonly {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--line: #ddd6c9;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--ok: #1a7f4e;
--ok-soft: #e3f5ec;
--bad: #b42318;
--bad-soft: #fbe9e7;
--display-font: 'Fraunces', Georgia, serif;
--body-font: 'Inter', -apple-system, sans-serif;
--mono-font: 'IBM Plex Mono', 'Courier New', monospace;
margin: 0 auto;
box-sizing: border-box;
font-family: var(--body-font);
color: var(--ink);
background: var(--paper);
}
.infographic-restonly[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--line: #2c313d;
--accent: #5b8def;
--accent-soft:#202942;
--ok: #5cd6a0;
--ok-soft: #16342a;
--bad: #e57373;
--bad-soft: #3a2222;
}
.infographic-restonly * { box-sizing: border-box; }
.infographic-restonly .ig-inner { padding: 1.75rem clamp(1.25rem, 5vw, 3rem) 2rem; }
.infographic-restonly .ig-title {
font-family: var(--display-font); font-size: clamp(1.4rem, 4.5vw, 1.9rem);
font-weight: 600; text-align: center; margin: 0 0 1.5rem;
}
/* Toggle switch */
.infographic-restonly .toggle-row {
display: flex; align-items: center; justify-content: center; gap: 0.75rem; margin-bottom: 1rem;
}
.infographic-restonly .toggle-label {
font-family: var(--mono-font); font-size: 0.82rem; font-weight: 700; color: var(--ink-soft); transition: color 0.2s; cursor: pointer;
}
.infographic-restonly .toggle-label.active.without { color: var(--bad); }
.infographic-restonly .toggle-label.active.with { color: var(--ok); }
.infographic-restonly .switch {
position: relative; width: 3.4rem; height: 1.8rem; border-radius: 999px; cursor: pointer;
background: var(--bad); transition: background 0.3s;
}
.infographic-restonly .switch.is-on { background: var(--ok); }
.infographic-restonly .switch .knob {
position: absolute; top: 0.18rem; left: 0.18rem; width: 1.44rem; height: 1.44rem; border-radius: 50%;
background: #fff; transition: transform 0.3s cubic-bezier(.4,1.4,.6,1); box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
.infographic-restonly .switch.is-on .knob { transform: translateX(1.6rem); }
.infographic-restonly .add-row { display: flex; justify-content: center; gap: 0.6rem; margin-bottom: 1.5rem; }
.infographic-restonly .add-btn {
font: inherit; font-family: var(--mono-font); font-size: 0.8rem; font-weight: 700; border: 1px dashed var(--line);
background: var(--paper-alt); color: var(--ink-soft); padding: 0.4rem 0.9rem; border-radius: 999px; cursor: pointer;
}
.infographic-restonly .add-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.infographic-restonly .add-btn:disabled { opacity: 0.5; cursor: default; }
.infographic-restonly .reset-btn {
font: inherit; font-family: var(--mono-font); font-size: 0.8rem; font-weight: 700; border: 1px solid var(--line);
background: transparent; color: var(--ink-soft); padding: 0.4rem 0.9rem; border-radius: 999px; cursor: pointer;
}
.infographic-restonly .reset-btn:hover { border-color: var(--accent); color: var(--accent); }
.infographic-restonly .section-tag {
font-family: var(--mono-font); font-size: 0.68rem; font-weight: 700; color: var(--ink-soft);
text-transform: uppercase; letter-spacing: 0.05em; margin: 1rem 0 0.4rem;
}
.infographic-restonly .section-tag:first-of-type { margin-top: 0; }
/* Code block */
.infographic-restonly .code-block {
font-family: var(--mono-font); font-size: 0.85rem; line-height: 1.75;
background: var(--paper-alt); border: 1px solid var(--line); border-radius: 6px;
padding: 0.9rem 1rem; white-space: pre;
}
.infographic-restonly .code-block .kw { color: var(--accent); }
.infographic-restonly .param-tok { transition: color 0.2s; }
.infographic-restonly .param-tok.without-tone { color: var(--bad); font-weight: 700; }
.infographic-restonly .param-tok.with-tone { color: var(--ok); font-weight: 700; }
.infographic-restonly .param-tok.pop { animation: ro-pop 0.3s ease; }
@keyframes ro-pop { 0% { opacity: 0; transform: scale(0.6); display: inline-block; } 100% { opacity: 1; transform: scale(1); } }
/* Chips row - straight line */
.infographic-restonly .chip-row {
display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap; margin: 1.1rem 0;
}
.infographic-restonly .guest-chip {
font-family: var(--mono-font); font-size: 0.75rem; font-weight: 700;
padding: 0.3rem 0.6rem; border-radius: 6px; opacity: 0; transform: translateY(-8px) scale(0.7);
transition: opacity 0.3s ease, transform 0.3s ease;
}
.infographic-restonly .guest-chip.without-tone { border: 1px solid var(--bad); color: var(--bad); background: var(--bad-soft); }
.infographic-restonly .guest-chip.with-tone { border: 1px solid var(--ok); color: var(--ok); background: var(--ok-soft); }
.infographic-restonly .guest-chip.is-in { opacity: 1; transform: translateY(0) scale(1); }
/* Call line - highlighted */
.infographic-restonly .call-line {
font-family: var(--mono-font); font-size: 0.82rem; text-align: center; font-weight: 700;
padding: 0.6rem 0.8rem; border-radius: 6px; margin-top: 0.5rem;
}
.infographic-restonly .call-line.without-tone { background: var(--bad-soft); color: var(--bad); border: 1px solid var(--bad); }
.infographic-restonly .call-line.with-tone { background: var(--ok-soft); color: var(--ok); border: 1px solid var(--ok); }
/* Output line - plain, no terminal chrome */
.infographic-restonly .output-line {
font-family: var(--mono-font); font-size: 0.85rem; text-align: center; margin-top: 0.7rem;
color: var(--ink-soft); opacity: 0; transform: translateY(4px); transition: opacity 0.3s ease, transform 0.3s ease;
}
.infographic-restonly .output-line.is-visible { opacity: 1; transform: translateY(0); }
.infographic-restonly .output-line .arrow { color: var(--ink-soft); margin-right: 0.4rem; }
.infographic-restonly .output-line .val { font-weight: 700; }
.infographic-restonly .output-line.without-tone .val { color: var(--bad); }
.infographic-restonly .output-line.with-tone .val { color: var(--ok); }
.infographic-restonly .caption {
text-align: center; font-size: 0.82rem; color: var(--ink-soft); margin-top: 1.1rem; min-height: 1.3em;
}
.infographic-restonly .caption strong { color: var(--ink); }
.infographic-restonly .caption.without-tone strong { color: var(--bad); }
.infographic-restonly .caption.with-tone strong { color: var(--ok); }
@media (prefers-reduced-motion: reduce) {
.infographic-restonly *, .infographic-restonly *::before, .infographic-restonly *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="ig-inner">
<h2 class="ig-title">The Rest Parameter</h2>
<div class="toggle-row">
<span class="toggle-label without active" id="labelWithout">without rest</span>
<div class="switch" id="switch"><div class="knob"></div></div>
<span class="toggle-label with" id="labelWith">with rest</span>
</div>
<div class="add-row">
<button class="add-btn" id="addBtn">+ Add param</button>
<button class="reset-btn" id="resetBtn">β² Reset</button>
</div>
<div class="section-tag">Function definition</div>
<div class="code-block" id="codeBlock"></div>
<div class="section-tag">Invocation</div>
<div class="call-line" id="callLine"></div>
<div class="section-tag">Output</div>
<div class="chip-row" id="chipRow"></div>
<div class="output-line" id="outputLine"><span class="arrow">β</span><span class="val" id="outputVal"></span></div>
<p class="caption" id="caption"></p>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-restonly')
|| document.querySelector('.infographic-restonly');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const ALL_GUESTS = ['Alice', 'Bob', 'Charlie', 'Dana', 'Evan', 'Frank', 'Grace', 'Henry'];
const ALL_PARAMS = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
const MAX = ALL_GUESTS.length;
const switchEl = wrapper.querySelector('#switch');
const labelWithout = wrapper.querySelector('#labelWithout');
const labelWith = wrapper.querySelector('#labelWith');
const addBtn = wrapper.querySelector('#addBtn');
const resetBtn = wrapper.querySelector('#resetBtn');
const codeBlock = wrapper.querySelector('#codeBlock');
const chipRow = wrapper.querySelector('#chipRow');
const callLine = wrapper.querySelector('#callLine');
const outputLine = wrapper.querySelector('#outputLine');
const outputVal = wrapper.querySelector('#outputVal');
const caption = wrapper.querySelector('#caption');
let isWith = false;
let count = 5;
function tone() { return isWith ? 'with-tone' : 'without-tone'; }
function render(justAdded) {
const guests = ALL_GUESTS.slice(0, count);
const params = ALL_PARAMS.slice(0, count);
const t = tone();
switchEl.classList.toggle('is-on', isWith);
labelWith.classList.toggle('active', isWith);
labelWithout.classList.toggle('active', !isWith);
addBtn.disabled = count >= MAX;
// Code block
if (isWith) {
codeBlock.innerHTML =
`<span class="kw">function</span> welcome(<span class="param-tok ${t}${justAdded ? ' pop' : ''}">...guests</span>) {\n` +
` console.log(<span class="param-tok ${t}">guests</span>);\n` +
`}`;
} else {
const paramList = params.map((p, i) =>
`<span class="param-tok ${t}${justAdded && i === params.length - 1 ? ' pop' : ''}">${p}</span>`
).join(', ');
codeBlock.innerHTML =
`<span class="kw">function</span> welcome(${paramList}) {\n` +
` console.log(${paramList});\n` +
`}`;
}
// Chips - straight line
chipRow.innerHTML = '';
guests.forEach((name, i) => {
const chip = document.createElement('span');
chip.className = `guest-chip ${t}`;
chip.textContent = name;
chipRow.appendChild(chip);
setTimeout(() => chip.classList.add('is-in'), 30 * i);
});
// Call line
callLine.className = `call-line ${t}`;
callLine.textContent = `welcome(${guests.map(g => `"${g}"`).join(', ')});`;
// Output line
outputLine.className = `output-line ${t}`;
outputVal.textContent = isWith
? `[ ${guests.map(g => `'${g}'`).join(', ')} ]`
: guests.join(' ');
outputLine.classList.remove('is-visible');
setTimeout(() => outputLine.classList.add('is-visible'), 30 * guests.length + 150);
// Caption
caption.className = `caption ${t}`;
if (isWith) {
caption.innerHTML = `<strong>...guests</strong> never changes β add as many as you like`;
} else {
caption.innerHTML = count >= MAX
? `<strong>${count} parameters</strong> β and the function + console.log both had to grow`
: `<strong>${count} parameters</strong> typed by hand β add one more and the code changes again`;
}
}
switchEl.addEventListener('click', () => { isWith = !isWith; render(false); });
labelWithout.addEventListener('click', () => { isWith = false; render(false); });
labelWith.addEventListener('click', () => { isWith = true; render(false); });
addBtn.addEventListener('click', () => {
if (count < MAX) { count++; render(true); }
});
resetBtn.addEventListener('click', () => {
count = 5;
render(false);
});
render(false);
})();
</script>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});π Object vs Array Destructuring with Rest
Feature | Object Destructuring | Array Destructuring |
|---|---|---|
Syntax |
|
|
Works With | Objects ( | Arrays ( |
Rest Result | Object | Array |
Order Matters? | β No | β Yes |
π§± Spread Operator
The Spread Operator expands an array or object into individual elements or properties.
Example
const numbers = [1, 2, 3];
const newNumbers = [...numbers, 4, 5];
console.log(newNumbers); // [1, 2, 3, 4, 5]π Explanation
...numbersexpands the array.The new array contains all elements of
numbersfollowed by4and5.
π Use Cases of Spread Operator
const a1 = [1, 2];
const a2 = [3, 4];
const merged = [...a1, ...a2];
console.log(merged); // [1, 2, 3, 4]π Explanation
...a1and...a2expand both arrays into one new array.
const original = [10, 20, 30];
const clone = [...original];
console.log(clone); // [10, 20, 30]π Explanation
...originalcreates a shallow copy of the array.cloneis a separate array fromoriginal.
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const combined = { ...obj1, ...obj2 };
console.log(combined); // { a: 1, b: 3, c: 4 }π Explanation
...obj1and...obj2combine both objects.If the same key exists, the value from the later object overwrites the earlier one.
<div class="infographic-spreadonly">
<style>
.infographic-spreadonly {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--line: #ddd6c9;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--ok: #1a7f4e;
--ok-soft: #e3f5ec;
--bad: #b42318;
--bad-soft: #fbe9e7;
--display-font: 'Fraunces', Georgia, serif;
--body-font: 'Inter', -apple-system, sans-serif;
--mono-font: 'IBM Plex Mono', 'Courier New', monospace;
margin: 0 auto;
box-sizing: border-box;
font-family: var(--body-font);
color: var(--ink);
background: var(--paper);
}
.infographic-spreadonly[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--line: #2c313d;
--accent: #5b8def;
--accent-soft:#202942;
--ok: #5cd6a0;
--ok-soft: #16342a;
--bad: #e57373;
--bad-soft: #3a2222;
}
.infographic-spreadonly * { box-sizing: border-box; }
.infographic-spreadonly .ig-inner { padding: 1.75rem clamp(1.25rem, 5vw, 3rem) 2rem; }
.infographic-spreadonly .ig-title {
font-family: var(--display-font); font-size: clamp(1.4rem, 4.5vw, 1.9rem);
font-weight: 600; text-align: center; margin: 0 0 1.5rem;
}
/* Toggle switch */
.infographic-spreadonly .toggle-row {
display: flex; align-items: center; justify-content: center; gap: 0.75rem; margin-bottom: 1rem;
}
.infographic-spreadonly .toggle-label {
font-family: var(--mono-font); font-size: 0.82rem; font-weight: 700; color: var(--ink-soft); transition: color 0.2s; cursor: pointer;
}
.infographic-spreadonly .toggle-label.active.without { color: var(--bad); }
.infographic-spreadonly .toggle-label.active.with { color: var(--ok); }
.infographic-spreadonly .switch {
position: relative; width: 3.4rem; height: 1.8rem; border-radius: 999px; cursor: pointer;
background: var(--bad); transition: background 0.3s;
}
.infographic-spreadonly .switch.is-on { background: var(--ok); }
.infographic-spreadonly .switch .knob {
position: absolute; top: 0.18rem; left: 0.18rem; width: 1.44rem; height: 1.44rem; border-radius: 50%;
background: #fff; transition: transform 0.3s cubic-bezier(.4,1.4,.6,1); box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
.infographic-spreadonly .switch.is-on .knob { transform: translateX(1.6rem); }
.infographic-spreadonly .add-row { display: flex; justify-content: center; gap: 0.6rem; margin-bottom: 1.5rem; }
.infographic-spreadonly .add-btn {
font: inherit; font-family: var(--mono-font); font-size: 0.8rem; font-weight: 700; border: 1px dashed var(--line);
background: var(--paper-alt); color: var(--ink-soft); padding: 0.4rem 0.9rem; border-radius: 999px; cursor: pointer;
}
.infographic-spreadonly .add-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.infographic-spreadonly .add-btn:disabled { opacity: 0.5; cursor: default; }
.infographic-spreadonly .reset-btn {
font: inherit; font-family: var(--mono-font); font-size: 0.8rem; font-weight: 700; border: 1px solid var(--line);
background: transparent; color: var(--ink-soft); padding: 0.4rem 0.9rem; border-radius: 999px; cursor: pointer;
}
.infographic-spreadonly .reset-btn:hover { border-color: var(--accent); color: var(--accent); }
.infographic-spreadonly .section-tag {
font-family: var(--mono-font); font-size: 0.68rem; font-weight: 700; color: var(--ink-soft);
text-transform: uppercase; letter-spacing: 0.05em; margin: 1rem 0 0.4rem;
}
.infographic-spreadonly .section-tag:first-of-type { margin-top: 0; }
/* Code block */
.infographic-spreadonly .code-block {
font-family: var(--mono-font); font-size: 0.85rem; line-height: 1.75;
background: var(--paper-alt); border: 1px solid var(--line); border-radius: 6px;
padding: 0.9rem 1rem; white-space: pre;
}
.infographic-spreadonly .code-block .kw { color: var(--accent); }
.infographic-spreadonly .tok { transition: color 0.2s; }
.infographic-spreadonly .tok.without-tone { color: var(--bad); font-weight: 700; }
.infographic-spreadonly .tok.with-tone { color: var(--ok); font-weight: 700; }
.infographic-spreadonly .tok.pop { animation: so-pop 0.3s ease; }
@keyframes so-pop { 0% { opacity: 0; transform: scale(0.6); } 100% { opacity: 1; transform: scale(1); } }
/* Chips row - straight line */
.infographic-spreadonly .chip-row {
display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap; margin: 1.1rem 0;
}
.infographic-spreadonly .val-chip {
font-family: var(--mono-font); font-size: 0.75rem; font-weight: 700;
padding: 0.3rem 0.6rem; border-radius: 6px; opacity: 0; transform: translateY(-8px) scale(0.7);
transition: opacity 0.3s ease, transform 0.3s ease;
}
.infographic-spreadonly .val-chip.without-tone { border: 1px solid var(--bad); color: var(--bad); background: var(--bad-soft); }
.infographic-spreadonly .val-chip.with-tone { border: 1px solid var(--ok); color: var(--ok); background: var(--ok-soft); }
.infographic-spreadonly .val-chip.is-in { opacity: 1; transform: translateY(0) scale(1); }
/* Call line - highlighted */
.infographic-spreadonly .call-line {
font-family: var(--mono-font); font-size: 0.82rem; text-align: center; font-weight: 700;
padding: 0.6rem 0.8rem; border-radius: 6px; margin-top: 0.5rem;
}
.infographic-spreadonly .call-line.without-tone { background: var(--bad-soft); color: var(--bad); border: 1px solid var(--bad); }
.infographic-spreadonly .call-line.with-tone { background: var(--ok-soft); color: var(--ok); border: 1px solid var(--ok); }
/* Output line - plain, no terminal chrome */
.infographic-spreadonly .output-line {
font-family: var(--mono-font); font-size: 0.85rem; text-align: center; margin-top: 0.7rem;
color: var(--ink-soft); opacity: 0; transform: translateY(4px); transition: opacity 0.3s ease, transform 0.3s ease;
}
.infographic-spreadonly .output-line.is-visible { opacity: 1; transform: translateY(0); }
.infographic-spreadonly .output-line .arrow { color: var(--ink-soft); margin-right: 0.4rem; }
.infographic-spreadonly .output-line .val { font-weight: 700; }
.infographic-spreadonly .output-line.without-tone .val { color: var(--bad); }
.infographic-spreadonly .output-line.with-tone .val { color: var(--ok); }
.infographic-spreadonly .caption {
text-align: center; font-size: 0.82rem; color: var(--ink-soft); margin-top: 1.1rem; min-height: 1.3em;
}
.infographic-spreadonly .caption strong { color: var(--ink); }
.infographic-spreadonly .caption.without-tone strong { color: var(--bad); }
.infographic-spreadonly .caption.with-tone strong { color: var(--ok); }
@media (prefers-reduced-motion: reduce) {
.infographic-spreadonly *, .infographic-spreadonly *::before, .infographic-spreadonly *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="ig-inner">
<h2 class="ig-title">The Spread Operator</h2>
<div class="toggle-row">
<span class="toggle-label without active" id="labelWithout">without spread</span>
<div class="switch" id="switch"><div class="knob"></div></div>
<span class="toggle-label with" id="labelWith">with spread</span>
</div>
<div class="add-row">
<button class="add-btn" id="addBtn">+ Add array</button>
<button class="reset-btn" id="resetBtn">β² Reset</button>
</div>
<div class="section-tag">Setup</div>
<div class="code-block" id="setupBlock"></div>
<div class="section-tag">Merge</div>
<div class="call-line" id="callLine"></div>
<div class="section-tag">Output</div>
<div class="chip-row" id="chipRow"></div>
<div class="output-line" id="outputLine"><span class="arrow">β</span><span class="val" id="outputVal"></span></div>
<p class="caption" id="caption"></p>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-spreadonly')
|| document.querySelector('.infographic-spreadonly');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const ALL_ARRAYS = [[1, 2], [3, 4], [5, 6], [7, 8]];
const NAMES = ['a1', 'a2', 'a3', 'a4'];
const MAX = ALL_ARRAYS.length;
const switchEl = wrapper.querySelector('#switch');
const labelWithout = wrapper.querySelector('#labelWithout');
const labelWith = wrapper.querySelector('#labelWith');
const addBtn = wrapper.querySelector('#addBtn');
const resetBtn = wrapper.querySelector('#resetBtn');
const setupBlock = wrapper.querySelector('#setupBlock');
const callLine = wrapper.querySelector('#callLine');
const chipRow = wrapper.querySelector('#chipRow');
const outputLine = wrapper.querySelector('#outputLine');
const outputVal = wrapper.querySelector('#outputVal');
const caption = wrapper.querySelector('#caption');
let isWith = false;
let count = 2;
function tone() { return isWith ? 'with-tone' : 'without-tone'; }
function render(justAdded) {
const arrays = ALL_ARRAYS.slice(0, count);
const names = NAMES.slice(0, count);
const merged = arrays.flat();
const t = tone();
switchEl.classList.toggle('is-on', isWith);
labelWith.classList.toggle('active', isWith);
labelWithout.classList.toggle('active', !isWith);
addBtn.disabled = count >= MAX;
// Setup block (array declarations) - identical in both modes
setupBlock.innerHTML = names.map((n, i) =>
`<span class="kw">const</span> ${n} = [${arrays[i].join(', ')}];`
).join('\n');
// Merge / invocation line
if (isWith) {
const spreads = names.map(n => `<span class="tok ${t}${justAdded && n === names[names.length - 1] ? ' pop' : ''}">...${n}</span>`).join(', ');
callLine.className = `call-line ${t}`;
callLine.innerHTML = `const merged = [${spreads}];`;
} else {
let chain = names[0];
let chainHtml = names[0];
for (let i = 1; i < names.length; i++) {
const isNew = justAdded && i === names.length - 1;
chainHtml += `<span class="tok ${t}${isNew ? ' pop' : ''}">.concat(${names[i]})</span>`;
}
callLine.className = `call-line ${t}`;
callLine.innerHTML = `const merged = ${chainHtml};`;
}
// Chips - straight line
chipRow.innerHTML = '';
merged.forEach((v, i) => {
const chip = document.createElement('span');
chip.className = `val-chip ${t}`;
chip.textContent = v;
chipRow.appendChild(chip);
setTimeout(() => chip.classList.add('is-in'), 30 * i);
});
// Output line
outputLine.className = `output-line ${t}`;
outputVal.textContent = `[ ${merged.join(', ')} ]`;
outputLine.classList.remove('is-visible');
setTimeout(() => outputLine.classList.add('is-visible'), 30 * merged.length + 150);
// Caption
caption.className = `caption ${t}`;
if (isWith) {
caption.innerHTML = `<strong>...${names[names.length - 1]}</strong> just gets added to the list β same pattern every time`;
} else {
caption.innerHTML = count >= MAX
? `<strong>${count - 1} .concat() calls</strong> chained β it keeps getting longer`
: `<strong>${count - 1} .concat() call${count > 2 ? 's' : ''}</strong> β add another array and the chain grows`;
}
}
switchEl.addEventListener('click', () => { isWith = !isWith; render(false); });
labelWithout.addEventListener('click', () => { isWith = false; render(false); });
labelWith.addEventListener('click', () => { isWith = true; render(false); });
addBtn.addEventListener('click', () => {
if (count < MAX) { count++; render(true); }
});
resetBtn.addEventListener('click', () => {
count = 2;
render(false);
});
render(false);
})();
</script>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});π° Difference Between Rest Parameter and Spread Operator
Feature | Rest Parameter | Spread Operator |
|---|---|---|
Purpose | Collects multiple values | Expands values |
Used In | Function parameters, destructuring | Arrays, objects, function calls |
Result | Array (or object in destructuring) | Individual elements or properties |
π Quick Recap
Rest Parameter π₯ | Spread Operator π€ |
|---|---|
Collects values | Expands values |
Packs multiple values into one variable | Unpacks values into individual elements |
Mainly used in function parameters | Mainly used for copying, merging, and expanding |
Easy Trick:
Rest collects everything together. π₯
Spread spreads everything apart. π€