Domů O mně Služby Portfolio Učím Blog Kontakt
← Zpět na učím

05.6 Spread u objektů

Výuka

Proč spread operátor?

Spread operátor ... u objektů "rozbalí" vlastnosti objektu do nového objektu. Je to moderní a elegantní způsob kopírování a spojování objektů.

Proč to potřebujeme?

  • Kopírování objektu - Rychlá a čistá kopie bez mutace
  • Spojování objektů - Sloučit více objektů do jednoho
  • Přepsání vlastností - Aktualizuj některé vlastnosti
  • Immutability - Vytvoř nový objekt místo změny originálu

Představ si to jako: Překopírování nálepek z jedné složky do druhé. Spread operátor ... vezme všechny nálepky (vlastnosti) a dá je do nové složky.

Jak to funguje?

Spread operátor u objektů (...obj):
1. "Rozbalí" vlastnosti objektu
2. { a: 1, b: 2 }  a: 1, b: 2
3. Používá se při vytváření nového objektu:
   - Kopie: { ...obj }
   - Spojení: { ...obj1, ...obj2 }
   - Přepsání: { ...obj, key: newValue }

Klíčové koncepty

  • Spread operátor ... - rozbalí vlastnosti objektu
  • Shallow copy - vytvoří kopii objektu (pozor u vnořených objektů!)
  • Immutability - neměnný způsob práce s objekty
  • Merging - spojování objektů
  • Overriding - přepsání vlastností (poslední vyhrává)
  • ES2018 feature - spread pro objekty od ES2018

JavaScript

Příklad 1: Kopírování objektu (shallow copy)

const original = {
  name: 'Alice',
  age: 25,
  city: 'Praha'
};

// Kopie pomocí spread operátoru
const copy = { ...original };
console.log(copy);
// → { name: 'Alice', age: 25, city: 'Praha' }

// Je to NOVÝ objekt
console.log(copy === original);
// → false (různé objekty)

// Změna kopie neovlivní originál
copy.age = 26;
console.log(original.age);  // → 25 (nezměněno)
console.log(copy.age);      // → 26

Co se stalo?

  • { ...original } vytvoří novou kopii objektu
  • Kopie je samostatný objekt (jiná reference)
  • Změny v kopii neovlivní originál
  • Pozor: Shallow copy - vnořené objekty se kopírují jako reference!

Příklad 2: Spojování objektů (merging)

const person = {
  name: 'Bob',
  age: 30
};

const contact = {
  email: 'bob@example.com',
  phone: '123456'
};

// Spojení dvou objektů
const user = { ...person, ...contact };
console.log(user);
// → { name: 'Bob', age: 30, email: 'bob@example.com', phone: '123456' }

// Můžeš spojit více objektů
const address = {
  city: 'Brno',
  zip: '60200'
};

const fullUser = { ...person, ...contact, ...address };
console.log(fullUser);
// → { name: 'Bob', age: 30, email: 'bob@example.com', phone: '123456', city: 'Brno', zip: '60200' }

Co se stalo?

  • { ...obj1, ...obj2 } sloučí objekty do jednoho nového
  • Můžeš kombinovat více objektů
  • Čitelnější než Object.assign()

Příklad 3: Přepsání vlastností (override)

const defaults = {
  theme: 'light',
  fontSize: 14,
  language: 'cs'
};

// Přepsání některých vlastností
const userSettings = {
  ...defaults,
  theme: 'dark',  // Přepíše theme
  fontSize: 16    // Přepíše fontSize
  // language zůstane 'cs'
};

console.log(userSettings);
// → { theme: 'dark', fontSize: 16, language: 'cs' }

// Pořadí záleží - poslední vyhrává
const config1 = { ...defaults, theme: 'dark' };
console.log(config1.theme);  // → dark

const config2 = { theme: 'dark', ...defaults };
console.log(config2.theme);  // → light (defaults přepsal dark!)

Co se stalo?

  • Poslední hodnota vyhrává - pokud klíč existuje vícekrát
  • { ...defaults, theme: 'dark' } - dark přepíše light
  • { theme: 'dark', ...defaults } - light přepíše dark
  • Pořadí záleží!

Příklad 4: Immutabilní update (React/Redux pattern)

const user = {
  name: 'Charlie',
  age: 35,
  city: 'Ostrava'
};

// ❌ Mutace - mění originál
user.age = 36;

// ✅ Immutabilní update - vytvoří nový objekt
const updatedUser = { ...user, age: 36 };
console.log(user.age);         // → 35 (nezměněno)
console.log(updatedUser.age);  // → 36

// Praktický příklad - React state update
const state = {
  user: { name: 'David', age: 40 },
  isLoading: false
};

// ✅ Immutabilní update
const newState = {
  ...state,
  isLoading: true
};

console.log(state.isLoading);     // → false (originál nezměněn)
console.log(newState.isLoading);  // → true

Co se stalo?

  • Spread vytváří nové objekty bez mutace originálu
  • Vhodné pro React/Redux (immutabilní state)
  • Alternativa k mutaci objektu

Příklad 5: Shallow copy - pozor u vnořených objektů!

const user = {
  name: 'Eve',
  age: 28,
  address: {
    city: 'Praha',
    zip: '11000'
  }
};

// Shallow copy
const copy = { ...user };

// Změň primitivní vlastnost - OK
copy.name = 'Eva';
console.log(user.name);  // → Eve (nezměněno)
console.log(copy.name);  // → Eva

// Změň vnořený objekt - POZOR!
copy.address.city = 'Brno';
console.log(user.address.city);  // → Brno (ZMĚNĚNO! ⚠️)
console.log(copy.address.city);  // → Brno

// Důvod: address je reference (sdílená)
console.log(user.address === copy.address);
// → true (stejná reference!)

Co se stalo?

  • Spread vytváří shallow copy - kopíruje jen první úroveň
  • Vnořené objekty se kopírují jako reference
  • Změna vnořeného objektu ovlivní originál!

Příklad 6: Deep copy - řešení pro vnořené objekty

const user = {
  name: 'Frank',
  age: 40,
  address: {
    city: 'Liberec',
    zip: '46001'
  }
};

// ❌ Shallow copy - vnořené objekty jsou reference
const shallowCopy = { ...user };
shallowCopy.address.city = 'Ústí nad Labem';
console.log(user.address.city);  // → Ústí nad Labem (změněno!)

// ✅ Deep copy - vnořené objekty ručně
const deepCopy = {
  ...user,
  address: { ...user.address }  // Kopie vnořeného objektu
};

deepCopy.address.city = 'Plzeň';
console.log(user.address.city);      // → Ústí nad Labem (nezměněno)
console.log(deepCopy.address.city);  // → Plzeň

// Alternativa - JSON.parse/stringify (pozor na ztrátu funkcí!)
const jsonDeepCopy = JSON.parse(JSON.stringify(user));
jsonDeepCopy.address.city = 'Karlovy Vary';
console.log(user.address.city);          // → Ústí nad Labem (nezměněno)
console.log(jsonDeepCopy.address.city);  // → Karlovy Vary

Co se stalo?

  • Pro deep copy musíš vnořené objekty ručně kopírovat
  • { ...obj, nested: { ...obj.nested } } - kopie vnořeného objektu
  • JSON.parse(JSON.stringify(obj)) - deep copy, ale ztratíš funkce!

TypeScript

TypeScript přidává typovou kontrolu spread operátoru.

Stejné příklady s typy

// Interface pro spread
interface User {
  name: string;
  age: number;
  city: string;
}

const user: User = {
  name: 'Alice',
  age: 25,
  city: 'Praha'
};

// Kopie s typy
const copy: User = { ...user };

// Spojování s typy
interface Contact {
  email: string;
  phone: string;
}

const contact: Contact = {
  email: 'alice@example.com',
  phone: '123456'
};

// Spojení - TypeScript pozná nový typ
const userWithContact = { ...user, ...contact };
// Type: { name: string; age: number; city: string; email: string; phone: string }

// Přepsání s typovou kontrolou
const updated: User = {
  ...user,
  age: 26  // ✅ OK - age je number
  // age: 'twenty-six'  // ❌ Error: Type 'string' is not assignable to type 'number'
};

// Partial update
function updateUser(user: User, updates: Partial<User>): User {
  return { ...user, ...updates };
}

const newUser = updateUser(user, { age: 27 });  // ✅ OK
// updateUser(user, { age: 'invalid' });  // ❌ Error: Type 'string' is not assignable to type 'number'

// Readonly
interface ReadonlyUser {
  readonly name: string;
  readonly age: number;
}

const readonlyUser: ReadonlyUser = { name: 'Bob', age: 30 };

// Spread vytvoří nový objekt (můžeš změnit)
const mutableCopy = { ...readonlyUser };
mutableCopy.age = 31;  // ✅ OK - kopie není readonly

// Nested types
interface Address {
  city: string;
  zip: string;
}

interface UserWithAddress {
  name: string;
  age: number;
  address: Address;
}

const userWithAddress: UserWithAddress = {
  name: 'Charlie',
  age: 35,
  address: { city: 'Brno', zip: '60200' }
};

// Deep copy s typy
const deepCopy: UserWithAddress = {
  ...userWithAddress,
  address: { ...userWithAddress.address }
};

TypeScript přidává:

  • Typovou kontrolu - spread musí odpovídat typu
  • Inference typů - TypeScript pozná výsledný typ
  • Partial<T> - pro částečné updaty
  • Prevenci chyb - upozorní na špatné typy

Rozdíl JS vs TS

JavaScript:

  • Spread funguje bez typové kontroly
  • Můžeš omylem přepsat vlastnosti špatným typem
  • Flexibilnější, ale nebezpečnější

TypeScript:

  • Spread má typovou kontrolu
  • TypeScript zkontroluje, že typy souhlasí
  • Bezpečnější, prevence chyb
// JavaScript - projde, ale problematické
const user = { name: 'Alice', age: 25 };
const updated = { ...user, age: 'twenty-six' };  // Špatný typ!

// TypeScript - kontrola
interface User {
  name: string;
  age: number;
}

const user: User = { name: 'Alice', age: 25 };
// const updated: User = { ...user, age: 'twenty-six' };  // ❌ Error: Type 'string' is not assignable to type 'number'

Tip

💡 Používej spread pro immutabilní operace:

// ❌ Mutace originálu
const user = { name: 'Alice', age: 25 };
user.age = 26;

// ✅ Immutabilní update
const updatedUser = { ...user, age: 26 };

// ✅ Vhodné pro React state
setState({ ...state, isLoading: true });

💡 Spread je shallow copy - pozor u vnořených objektů:

const obj = { a: 1, nested: { b: 2 } };

// ❌ Shallow copy - nested je reference
const copy = { ...obj };
copy.nested.b = 3;  // Změní i originál!

// ✅ Deep copy vnořených objektů
const deepCopy = {
  ...obj,
  nested: { ...obj.nested }
};

💡 Pořadí záleží - poslední vyhrává:

// ❌ defaults přepíše user hodnoty
const config = { ...user, ...defaults };

// ✅ user hodnoty přepíší defaults
const config = { ...defaults, ...user };

Kvíz

Co vypíše tento kód?

const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const result = { ...obj1, ...obj2 };
console.log(result.b);

- To by bylo, kdyby obj2 byl první

- - { ...obj1, ...obj2 } sloučí objekty

  • obj1{ a: 1, b: 2 }
  • obj2{ b: 3, c: 4 }
  • Vlastnost b existuje v obou objektech
  • Poslední hodnota vyhrává: obj2.b (= 3) přepíše obj1.b (= 2)
  • result = { a: 1, b: 3, c: 4 }

- Spread nespojuje hodnoty do pole

- Není to chyba, spread je validní syntaxe

Důležité: Při spojování objektů poslední hodnota vyhrává - pokud klíč existuje vícekrát!

Důležité: Při spojování objektů poslední hodnota vyhrává - pokud klíč existuje vícekrát!

🎯 Závěrečný projekt

Po dokončení všech 8 dílů vytvoříte jednoduchou Todо aplikaci v čistém JavaScriptu. Naučíte se, jak aplikovat vše, co jste se naučili, na reálný projekt.

Zobrazit podrobnosti projektu →

Připraveni začít?

Zaregistrujte se a získejte přístup ke všem dílům tohoto seriálu

Kontaktujte mě

Informace o seriálu

Obtížnost

Délka

Cca 480 minut

Počet videí

8 videí + projekty

Certifikát

Po dokončení obdržíte certifikát


Lekce v této sekci


Struktura lekcí (souborový strom)

06. Typescript specifika
  • v přípravě
08. Moduly tridy
  • v přípravě
09. React zaklady
  • v přípravě
10. React hooks
  • v přípravě
12. Nextjs server
  • v přípravě
13. Databaze auth
  • v přípravě
14. Nextjs pokrocile
  • v přípravě