kodingup.camp

Beranda/JavaScript Mastery: Object & Data

Mengelola Object secara Dinamis

Modul 3 dari 144 menit bacaDynamic Object Management

Isi modul

    Kenapa ini penting

    Data di dunia nyata jarang lengkap dan jarang tetap. Respons dari API bisa punya field yang kadang ada kadang tidak. Form pendaftaran bisa punya kolom opsional. Pengaturan aplikasi bisa bertambah seiring waktu. Kamu perlu cara untuk menambah, menghapus, dan memeriksa properti dengan aman, serta cara untuk mengunci object yang tidak boleh diubah, misalnya konfigurasi atau daftar konstanta.

    Tanpa teknik ini, kamu akan sering melihat error terkenal TypeError: Cannot read properties of undefined, yang muncul saat kode mencoba membaca data.user.alamat.kota padahal alamat tidak ada. Modul ini mengajarkan cara mencegahnya dengan optional chaining dan pemeriksaan properti yang benar.

    Konsep inti

    Menambah, mengubah, menghapus properti

    Memeriksa keberadaan properti

    Ada beberapa cara, dan masing-masing punya makna yang sedikit berbeda:

    Cara Mengecek Catatan
    obj.kunci !== undefined nilainya bukan undefined salah jika properti ada tapi nilainya memang undefined
    "kunci" in obj ada di object atau prototype-nya "toString" in {} bernilai true
    Object.hasOwn(obj, "kunci") ada sebagai properti milik sendiri cara modern yang direkomendasikan
    obj.hasOwnProperty("kunci") sama seperti hasOwn gagal untuk object tanpa prototype

    Apa itu prototype? Setiap object punya "orang tua" tempat ia mewarisi method. Object biasa mewarisi dari Object.prototype, makanya {} punya method toString walaupun kamu tidak menulisnya. Operator in ikut mencari ke orang tua; Object.hasOwn tidak.

    Mengunci object: freeze, seal, preventExtensions

    Method Tambah properti Hapus properti Ubah nilai
    Object.preventExtensions tidak ya ya
    Object.seal tidak tidak ya
    Object.freeze tidak tidak tidak

    Analogi: seal seperti kotak bekal yang tutupnya dilem, isinya boleh diganti tapi jumlah sekatnya tetap. freeze seperti kotak bekal yang dibungkus plastik keras: tidak bisa diapa-apakan.

    Di strict mode, melanggar aturan ini melempar TypeError. Di mode non-strict, pelanggaran gagal diam-diam (tidak ada error, tapi tidak terjadi apa-apa), yang lebih berbahaya karena bug-nya tidak terlihat. Penting juga: freeze itu dangkal. Object di dalam object yang dibekukan masih bisa diubah, kecuali kamu membekukannya juga secara rekursif. Cek status dengan Object.isFrozen dan Object.isSealed.

    Optional chaining dan nullish coalescing

    Contoh kode

    "use strict";
    
    // 1) Tambah, ubah, hapus
    const user = { nama: "Agus", email: "agus@mail.id", password: "rahasia" };
    user.umur = 25;               // tambah
    user["kota asal"] = "Solo";   // kunci dengan spasi
    const field = "email";
    user[field] = "agus@baru.id"; // kunci dari variabel
    console.log(delete user.umur); // → true
    console.log(user);
    // → { nama: 'Agus', email: 'agus@baru.id', password: 'rahasia', 'kota asal': 'Solo' }
    
    // 2) Menghapus tanpa mengubah asli (rest destructuring)
    const { password, ...userPublik } = user;
    console.log(userPublik); // → { nama: 'Agus', email: 'agus@baru.id', 'kota asal': 'Solo' }
    console.log("password" in user); // → true (asli tetap utuh)
    
    // 3) in vs Object.hasOwn
    const kosong = {};
    console.log("toString" in kosong);              // → true  (warisan prototype)
    console.log(Object.hasOwn(kosong, "toString")); // → false (bukan milik sendiri)
    
    const data = { diskon: undefined };
    console.log(data.diskon !== undefined);     // → false (menipu: propertinya ada)
    console.log(Object.hasOwn(data, "diskon")); // → true
    
    // Object tanpa prototype: hasOwnProperty tidak tersedia, hasOwn tetap jalan
    const bersih = Object.create(null);
    bersih.a = 1;
    console.log(Object.hasOwn(bersih, "a"));   // → true
    console.log(typeof bersih.hasOwnProperty); // → undefined
    
    // 4) freeze dan seal
    const CONFIG = Object.freeze({ api: "https://api.toko.id", retry: 3, fitur: { chat: true } });
    try {
      CONFIG.retry = 10;
    } catch (err) {
      console.log(err.constructor.name); // → TypeError
    }
    CONFIG.fitur.chat = false;            // freeze dangkal: bagian dalam masih bisa diubah
    console.log(CONFIG.fitur.chat);       // → false
    console.log(Object.isFrozen(CONFIG)); // → true
    
    const kursi = Object.seal({ nomor: "A1", terisi: false });
    kursi.terisi = true;   // boleh, ubah nilai
    try {
      kursi.harga = 50000; // tidak boleh, tambah properti
    } catch (err) {
      console.log("Tidak bisa menambah properti pada object seal");
    }
    console.log(kursi); // → { nomor: 'A1', terisi: true }
    
    // 5) Deep freeze sederhana (rekursif)
    function deepFreeze(obj) {
      for (const nilai of Object.values(obj)) {
        if (typeof nilai === "object" && nilai !== null) deepFreeze(nilai);
      }
      return Object.freeze(obj);
    }
    const TEMA = deepFreeze({ warna: { utama: "#0057ff" } });
    console.log(Object.isFrozen(TEMA.warna)); // → true
    
    // 6) Optional chaining dan ??
    const respons = { user: { nama: "Lina", alamat: null, poin: 0 } };
    console.log(respons.user?.alamat?.kota);        // → undefined (tanpa error)
    console.log(respons.user?.alamat?.kota ?? "-"); // → -
    console.log(respons.user.poin || 100);          // → 100 (salah! 0 dianggap kosong)
    console.log(respons.user.poin ?? 100);          // → 0   (benar)
    console.log(respons.user.sapa?.());             // → undefined (method tidak ada, aman)
    const kunciDinamis = "nama";
    console.log(respons.user?.[kunciDinamis]);      // → Lina
    

    Kesalahan umum

    1. Memeriksa keberadaan properti dengan if (obj.kunci). Nilai 0, "", atau false dianggap tidak ada. Pakai Object.hasOwn(obj, "kunci").
    2. Memakai in untuk object dari data luar. "constructor" in {} bernilai true. Kalau kamu hanya peduli properti milik sendiri, pakai Object.hasOwn.
    3. Mengira Object.freeze membekukan sampai dalam. Freeze itu dangkal; bekukan object bersarang secara rekursif kalau perlu.
    4. Memakai || untuk nilai default angka. stok || 10 mengubah stok 0 menjadi 10. Pakai stok ?? 10.
    5. Menebar ?. di mana-mana. Optional chaining menyembunyikan error. Pakai hanya pada bagian yang memang boleh kosong, bukan pada data yang seharusnya selalu ada.

    Latihan

    1. Mudah, Diberikan const produk = { nama: "Buku", harga: 0 }. Tampilkan harga dengan default "Gratis" hanya jika harga null/undefined (hasil yang benar: 0).
    2. Sedang, Buat function hapusKunci(obj, kunci) yang mengembalikan object baru tanpa kunci tersebut, tanpa mengubah object asli. Kunci diberikan sebagai string variabel.
    3. Menantang, Buat function ambil(obj, path) yang membaca nilai bersarang berdasarkan teks seperti "user.alamat.kota", mengembalikan undefined (bukan error) kalau ada bagian yang kosong. Gunakan split dan optional chaining.
    Lihat solusi
    // 1) Mudah
    const produk = { nama: "Buku", harga: 0 };
    console.log(produk.harga ?? "Gratis"); // → 0
    
    // 2) Sedang
    function hapusKunci(obj, kunci) {
      const { [kunci]: _dibuang, ...sisa } = obj; // destructuring dengan computed key
      return sisa;
    }
    const asli = { a: 1, b: 2, c: 3 };
    console.log(hapusKunci(asli, "b")); // → { a: 1, c: 3 }
    console.log(asli);                  // → { a: 1, b: 2, c: 3 }
    
    // 3) Menantang
    function ambil(obj, path) {
      let hasil = obj;
      for (const bagian of path.split(".")) {
        hasil = hasil?.[bagian];
      }
      return hasil;
    }
    const data = { user: { alamat: { kota: "Makassar" } } };
    console.log(ambil(data, "user.alamat.kota")); // → Makassar
    console.log(ambil(data, "user.profil.foto")); // → undefined
    

    Ringkasan

    Kuis pemahaman

    Pilih satu jawaban per soal. Penjelasan muncul setelah kamu menjawab.