Kihagyás

Az object adattípus

A JavaScript egyik beépített adattípusa az object (objektum). Ennek segítségével komplex entitásokat tudunk létrehozni, amelyekbe property-érték párokat helyezhetünk.

Object létrehozása

JavaScriptben objektumokat kétféleképpen hozhatunk létre:

  • a new Object() konstruktor használatával
  • a kapcsos zárójel ({}) operátorok segítségével.

Példa: Üres object-ek létrehozása

1
2
let empty1 = new Object();
let empty2 = {};

A továbbiakban a kapcsos zárójeles szintaxist fogjuk használni a jegyzetben. A kapcsos zárójel között, vesszővel elválasztva megadhatjuk az adott objektum property-érték párjait property: érték formában.

Példa: Egy személy object létrehozása

1
2
3
4
5
6
let person = {
    name: "Bob",
    age: 30,
    occupation: "programmer",
    married: false
};

Modern object létrehozási módszerek

Ha a változó neve megegyezik a property nevével, akkor nem kell megismételni, ez a shorthand property syntax.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
let name = "Bob";
let age = 30;
let occupation = "programmer";

// Old way
let person1 = {
    name: name,
    age: age,
    occupation: occupation
};

// Modern shorthand way
let person2 = {
    name,
    age,
    occupation
};

console.log(person2);  // same result as person1

Object elemeinek elérése

Egy object adott property-jéhez tartozó értéke kétféleképpen is lekérdezhető:

  • a . (pont) operátorral
  • a [] (szögletes zárójel) operátorral.

Példa: A name property-hez tartozó érték lekérése

1
2
3
4
let person = { name: "Bob", age: 30, occupation: "programmer" };

console.log(person.name);      // . operator
console.log(person["name"]);   // [] operator

Kimenet

Bob Bob

A lényegi különbség a két operátor működésében, hogy amennyiben változót adunk át nekik, akkor a . operátor a változó nevét tekinti kulcsnak, míg a [] operátor a változó értékét fogja kulcsként venni.

Példa: A . és [] operátorok közötti működésbeli különbség.

1
2
3
4
5
let person = { name: "Bob", age: 30, occupation: "programmer" };
let something = "age";

console.log(person.something);
console.log(person[something]);

Kimenet

undefined 30

A példában az első esetben a . operátor egy something nevű property-t keresett az objektumon belül, amit nem talált meg, így undefined értéket kaptunk. A második esetben a [] operátor, már a something változóban szerepelő értéket, az age property-t kereste, és mivel van ilyen property-nk az objektumunkban, ezért az ahhoz tartozó értéket kaptuk vissza.

Optional Chaining (?.)

Modern JavaScriptben biztonságosan érhetünk el mélyen egymásba ágyazott property-ket anélkül, hogy ellenőriznünk kellene minden szinten, hogy létezik-e:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let user = {
    name: "Alice",
    address: {
        city: "Budapest",
        zipCode: 1011
    }
};

let user2 = {
    name: "Bob"
    // no address property
};

// Old way - risky!
console.log(user.address.city);        // "Budapest"
// console.log(user2.address.city);    // ERROR! Cannot read property 'city' of undefined

// Old way - safe but verbose
console.log(user2.address ? user2.address.city : undefined);  // undefined

// Modern way with optional chaining
console.log(user.address?.city);       // "Budapest"
console.log(user2.address?.city);      // undefined - no error!
console.log(user2.address?.city?.toString());  // undefined - safe chaining

Nullish Coalescing (??)

Az ?? operátor akkor ad vissza default értéket, ha a bal oldali érték null vagy undefined:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
let user = {
    name: "David",
    age: 0,
    city: null
};

// Old way with || (problematic with falsy values)
console.log(user.age || 18);           // 18 - wrong! (0 is falsy)
console.log(user.city || "Unknown");   // "Unknown" - correct

// Modern way with ?? (only null/undefined)
console.log(user.age ?? 18);           // 0 - correct! (0 is not null/undefined)
console.log(user.city ?? "Unknown");   // "Unknown" - correct
console.log(user.country ?? "Hungary"); // "Hungary" - correct

// Combining with optional chaining
console.log(user.address?.city ?? "No city");  // "No city"

Fontosabb object műveletek

Property előfordulásának ellenőrzése

Az in operátorral tudjuk a megbízhatóan ellenőrizni, hogy egy property szerepel-e az objektumban:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let person = { name: "Bob", age: 30, occupation: "programmer" };

if ("occupation" in person) {
    console.log("Has occupation property!");
    console.log("Value: " + person.occupation);
}

if ("salary" in person) {
    console.log("Has salary property!");
} else {
    console.log("No salary property found.");
}

Kimenet

Has occupation property!

Value: programmer

No salary property found.

Miért jobb az in operátor mint az undefined ellenőrzés?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let person = { name: "Bob", age: 30, occupation: undefined };

// This won't work correctly
if (person.occupation !== undefined) {
    console.log("Has occupation property!");  // doesn't print - wrong!
}

// This works correctly
if ("occupation" in person) {
    console.log("Has occupation property!");  // prints - correct!
}

Kimenet

Has occupation property!

Érték módosítása

Az object egy adott property-jéhez tartozó értékét módosíthatjuk is. Hivatkozunk a módosítani kívánt elemre (a fentebb tárgyalt . vagy [] operátorok valamelyikével), majd az = operátorral beállítjuk az új értéket.

1
2
3
4
let person = { name: "Bob", age: 30, occupation: "programmer" };
person.occupation = "astronaut";  // person["occupation"] = "astronaut" also works

console.log(person.name + " changed jobs, now an " + person.occupation + "!");

Kimenet

Bob changed jobs, now an astronaut!

Új property-érték pár beszúrása

Az objektumainkba bármikor beszúrhatunk új property-érték párokat. A beszúrás szintaxisa nagyon egyszerű: megadjuk az object beszúrni kívánt property-jének a nevét, majd = operátorral értéket adunk neki.

1
2
3
4
let person = { name: "Bob", age: 30, occupation: "programmer" };
person.favoriteColor = "transparent";

console.log(person);

Kimenet

{

name: 'Bob',

age: 30,

occupation: 'programmer',

favoriteColor: 'transparent'

}

Property-érték pár törlése

A delete kulcsszóval lehetőségünk van egy adott property-t és a hozzá tartozó értéket törölni az object-ből.

1
2
3
4
let person = { name: "Bob", age: 30, occupation: "programmer" };
delete person.occupation;

console.log(person);

Kimenet

{ name: 'Bob', age: 30 }

Object destructuring

Hasonlóan a tömbök destruktúrálásához, az objektumokból is kinyerhetünk property-ket változókba:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
let person = {
    name: "Alice",
    age: 25,
    occupation: "designer",
    city: "Budapest"
};

// Old way
let name = person.name;
let age = person.age;

// Modern destructuring way
let {name, age, occupation} = person;
console.log(name);        // "Alice"
console.log(age);         // 25
console.log(occupation);  // "designer"

// Rename variables during destructuring
let {name: personName, age: personAge} = person;
console.log(personName);  // "Alice"
console.log(personAge);   // 25

// Default values
let {name, country = "Hungary"} = person;
console.log(country);     // "Hungary" (not in original object)

// Rest operator for remaining properties
let {name, ...rest} = person;
console.log(name);        // "Alice"
console.log(rest);        // {age: 25, occupation: "designer", city: "Budapest"}

Object-ek bejárása

Az object-ek property-érték párjának bejárása legegyszerűbben egy for-ciklussal tehető meg. Az alábbi módszerek mindegyike ezt valósítja meg (amelyik a legszimpatikusabb a számunkra, azt használjuk).

for-in szerkezet

1
2
3
4
5
let person = { name: "Bob", age: 30, occupation: "programmer" };

for (let key in person) {
    console.log(key + " value: " + person[key]);
}

for-of szerkezet Object.keys() használatával

1
2
3
4
5
let person = { name: "Bob", age: 30, occupation: "programmer" };

for (let key of Object.keys(person)) {
    console.log(key + " value: " + person[key]);
}

for-of szerkezet Object.entries() használatával (ajánlott)

1
2
3
4
5
let person = { name: "Bob", age: 30, occupation: "programmer" };

for (let [key, value] of Object.entries(person)) {
    console.log(key + " value: " + value);
}

Object.values() - csak az értékek

1
2
3
4
5
let person = { name: "Bob", age: 30, occupation: "programmer" };

for (let value of Object.values(person)) {
    console.log(value);
}

Kimenet

Bob

30

programmer

Spread operátor objektumokkal

A spread operátor (...) használatával másolhatunk és egyesíthetünk objektumokat:

Objektum másolása (shallow copy)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
let original = {
    name: "Alice",
    age: 25,
    city: "Budapest"
};

// Create a copy
let copy = {...original};
copy.age = 26;

console.log(original.age);  // 25 - unchanged
console.log(copy.age);      // 26 - changed

// Add new properties while copying
let extended = {
    ...original,
    occupation: "designer",
    country: "Hungary"
};

console.log(extended);
// {name: "Alice", age: 25, city: "Budapest", occupation: "designer", country: "Hungary"}

Objektumok egyesítése

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let personal = {
    name: "Bob",
    age: 30
};

let work = {
    occupation: "programmer",
    company: "TechCorp"
};

let contact = {
    email: "bob@example.com",
    phone: "123-456-7890"
};

// Merge objects
let fullProfile = {...personal, ...work, ...contact};
console.log(fullProfile);

// Later properties override earlier ones
let obj1 = {a: 1, b: 2};
let obj2 = {b: 3, c: 4};
let merged = {...obj1, ...obj2};
console.log(merged);  // {a: 1, b: 3, c: 4} - obj2.b overrides obj1.b

Override properties

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
let user = {
    name: "Charlie",
    age: 28,
    active: false
};

// Update specific properties
let updatedUser = {
    ...user,
    active: true,
};

console.log(updatedUser);

Hasznos Object metódusok (érdekesség)

Object.keys(), Object.values(), Object.entries()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
let student = {
    name: "Diana",
    age: 22,
    major: "Computer Science",
    gpa: 3.8
};

// Get all property names
console.log(Object.keys(student));
// ["name", "age", "major", "gpa"]

// Get all values
console.log(Object.values(student));
// ["Diana", 22, "Computer Science", 3.8]

// Get all key-value pairs
console.log(Object.entries(student));
// [["name", "Diana"], ["age", 22], ["major", "Computer Science"], ["gpa", 3.8]]

// Count properties
console.log(Object.keys(student).length);  // 4

Object.freeze() - objektum "befagyasztása"

Az Object.freeze() segítségével létrehozhatunk változtathatatlan (immutable) objektumokat:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
let config = {
    apiUrl: "https://api.example.com",
    timeout: 5000,
    retries: 3
};

Object.freeze(config);

// These modifications won't work (in strict mode: error)
config.timeout = 10000;  // no effect
config.newProp = "test"; // no effect
delete config.retries;   // no effect

console.log(config);
// {apiUrl: "https://api.example.com", timeout: 5000, retries: 3} - unchanged

Figyelem

Az Object.freeze() csak "shallow" (sekély) befagyasztást végez. Ha az objektum beágyazott objektumokat tartalmaz, azok még módosíthatók lesznek!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
let user = {
    name: "Eve",
    settings: {
        theme: "dark",
        language: "en"
    }
};

Object.freeze(user);

user.name = "Eva";  // no effect
user.settings.theme = "light";  // THIS WORKS! - nested object not frozen

console.log(user.settings.theme);  // "light"

Object.seal() - objektum "lezárása"

Az Object.seal() megengedni a meglévő property-k módosítását, de nem engedélyezi új property-k hozzáadását vagy meglévők törlését:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let user = {
    name: "Frank",
    age: 35
};

Object.seal(user);

user.age = 36;           // OK - can modify existing properties
user.city = "Budapest";  // no effect - can't add new properties
delete user.name;        // no effect - can't delete properties

console.log(user);  // {name: "Frank", age: 36}

Objektumok összehasonlítása

Fontos: Az objektumokat JavaScriptben referencia szerint hasonlítja össze a nyelv, nem érték szerint!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let obj1 = {name: "Alice", age: 25};
let obj2 = {name: "Alice", age: 25};
let obj3 = obj1;  // same reference

console.log(obj1 === obj2);  // false - different objects, even if content is same
console.log(obj1 === obj3);  // true - same reference

// Modify through reference
obj3.age = 26;
console.log(obj1.age);  // 26 - obj1 also changed!

Érték szerinti összehasonlítás:

Ha érték szerint szeretnénk két objektumot összehasonlítani, akkor sajnos (vagy nem) végig kell szépen néznünk a kulcsokat, és megvizsgálni a tartalmazást.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
function objectsEqual(obj1, obj2) {
    let keys1 = Object.keys(obj1);
    let keys2 = Object.keys(obj2);

    if (keys1.length !== keys2.length) {
        return false;
    }

    for (let key of keys1) {
        if (obj1[key] !== obj2[key]) {
            return false;
        }
    }

    return true;
}

let obj1 = {name: "Alice", age: 25};
let obj2 = {name: "Alice", age: 25};
console.log(objectsEqual(obj1, obj2));  // true

JSON művletek

A JavaScript Object Notation (JSON) szabványos formátum adatok tárolására és átvitelére:

JSON.stringify() - objektumból string

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
let student = {
    name: "Hannah",
    age: 21,
    grades: [85, 90, 88],
    active: true
};

// Convert to JSON string
let jsonString = JSON.stringify(student);
console.log(jsonString);
// {"name":"Hannah","age":21,"grades":[85,90,88],"active":true}

// Pretty print with indentation
let prettyJson = JSON.stringify(student, null, 2);
console.log(prettyJson);
/*
{
  "name": "Hannah",
  "age": 21,
  "grades": [
    85,
    90,
    88
  ],
  "active": true
}
*/

// Save to localStorage (browser)
// localStorage.setItem('student', JSON.stringify(student));

JSON.parse() - stringből objektum

1
2
3
4
5
6
7
8
9
let jsonString = '{"name":"Hannah","age":21,"grades":[85,90,88],"active":true}';

// Convert from JSON string
let student = JSON.parse(jsonString);
console.log(student.name);   // "Hannah"
console.log(student.grades); // [85, 90, 88]

// Read from localStorage (browser)
// let student = JSON.parse(localStorage.getItem('student'));

Gyakorlati példa: API kommunikáció

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Sending data to server
let userData = {
    username: "john_doe",
    email: "john@example.com",
    age: 28
};

// Convert to JSON for sending
let jsonData = JSON.stringify(userData);
// Send jsonData to server via fetch/axios...

// Receiving data from server
let serverResponse = '{"status":"success","userId":12345,"message":"User created"}';
let response = JSON.parse(serverResponse);

if (response.status === "success") {
    console.log("User ID: " + response.userId);
}

Objektumok tömbökkel

Nagyon gyakori minta, hogy objektumok tömbjeit kezeljük:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
let students = [
    {name: "Alice", age: 20, grade: 85},
    {name: "Bob", age: 22, grade: 92},
    {name: "Charlie", age: 19, grade: 78},
    {name: "Diana", age: 21, grade: 95}
];

// Find student by name
let student = students.find(s => s.name === "Bob");
console.log(student);  // {name: "Bob", age: 22, grade: 92}

// Filter students with grade > 80
let goodStudents = students.filter(s => s.grade > 80);
console.log(goodStudents);

// Get all names
let names = students.map(s => s.name);
console.log(names);  // ["Alice", "Bob", "Charlie", "Diana"]

// Sort by grade (descending)
let sorted = [...students].sort((a, b) => b.grade - a.grade);
console.log(sorted);

Úton az objektumorientált paradigma felé

Az object egy hasznos, viszonylag gyakran használt adatszerkezet JavaScriptben.

Ha nem 1 személyünk lenne, hanem mondjuk 100, akkor 100 különböző személy object-et kellene kézzel létrehoznunk, amelyekben külön-külön megadnánk a property-érték párokat.

Ezt persze nem szeretnénk manuálisan elvégezni, így felmerül az igény arra, hogy hozzunk létre egy "blueprint"-et, ami megmondja, hogy hogyan fognak kinézni az objektumaink. Ez a "blueprint" lesz a más nyelvekből ismerős osztály. Ha van egy Person osztályunk, abból már egyszerűen tudunk személy objektumokat készíteni a példányosítás során.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Without classes - repetitive
let person1 = {
    name: "Alice",
    age: 25,
    greet() {
        console.log(`Hi, I'm ${this.name}`);
    }
};

let person2 = {
    name: "Bob",
    age: 30,
    greet() {
        console.log(`Hi, I'm ${this.name}`);
    }
};

// With classes - reusable blueprint (következő gyakorlaton)
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    greet() {
        console.log(`Hi, I'm ${this.name}`);
    }
}

let person1 = new Person("Alice", 25);
let person2 = new Person("Bob", 30);

Az osztályokról, illetve az objektumorientáltság JavaScriptben való megvalósításáról a következő gyakorlaton fogunk tanulni.