Masukkan Password
57 Value vs Reference Type in Practice
Lecture ini sangat penting karena mulai membahas salah satu hal yang sering membuat pemula JavaScript bingung:
Kenapa ketika dua variabel berisi object yang kelihatannya sama,
===bisa menghasilkanfalse? Dan kenapa ketika kita mengubah array lewat variabel kedua, array pertama ikut berubah?
Kuncinya adalah memahami perbedaan value type dan reference type, terutama saat melakukan assignment dan comparison.
1. Gambaran besar
Di JavaScript, secara sederhana kita bisa membagi data menjadi:
VALUE TYPE
→ primitive
→ Number
→ String
→ Boolean
→ Undefined
→ Null
→ BigInt
→ Symbol
dan:
REFERENCE TYPE
→ Object
→ Array
→ Function
→ dll.
Perbedaan paling penting:
| Value Type | Reference Type |
|---|---|
| Menyimpan nilai | Menyimpan reference ke object |
| Assignment menyalin value | Assignment menyalin reference |
| Comparison membandingkan value | Comparison object membandingkan reference |
Contoh: 30, "John", true | Contoh: {}, [], function |
| Biasanya perubahan satu variabel tidak memengaruhi variabel lain | Perubahan object yang sama bisa terlihat dari beberapa variabel |
Kalau bingung dengan istilah reference, gunakan analogi ini:
Value type = fotokopi
x = 30
y = x
Bayangkan:
x → [30]
y → [30]
y mendapatkan salinan 30.
Reference type = alamat rumah
Misalnya:
obj1 = { age: 28 };
obj2 = obj1;
Bayangkan:
obj1 ──┐
↓
🏠 Rumah A
↑
obj2 ──┘
obj2 bukan mendapatkan rumah baru.
obj2 mendapatkan alamat rumah yang sama.
Ini adalah inti seluruh lecture.
2. Contoh pertama: Number
Lecture dimulai dengan:
let x = 30;
let y = 30;
console.log(x == y);
Hasil:
true
Kenapa?
Karena 30 adalah Number, dan Number adalah primitive/value type.
Secara mental model:
x → 30
y → 30
Kemudian:
x == y
JavaScript membandingkan nilainya:
30 == 30
↓
true
Jadi bukan membandingkan “apakah x dan y menunjuk ke tempat yang sama”.
Yang dibandingkan adalah value.
3. Bagaimana kalau menggunakan ===?
Sebenarnya untuk kasus ini:
let x = 30;
let y = 30;
console.log(x === y);
hasilnya juga:
true
Karena:
x → 30
y → 30
30 === 30
Jadi untuk primitive seperti Number, String, Boolean, dan sebagainya, === membandingkan nilai dan tipe.
Saya sarankan mulai sekarang biasakan menggunakan:
===
daripada:
==
kecuali memang ada alasan khusus menggunakan loose equality.
4. Sekarang kita masuk ke bagian yang sering membingungkan
Perhatikan:
let obj1 = {
age: 28
};
let obj2 = {
age: 28
};
console.log(obj1 === obj2);
Banyak pemula akan berpikir:
“Kan isinya sama-sama
{ age: 28 }, harusnyatruedong?”
Tapi hasilnya:
false
Kenapa?
Karena obj1 dan obj2 adalah object.
Object adalah reference type.
5. Jangan berpikir object seperti Number
Untuk Number:
let x = 30;
let y = 30;
mental model sederhananya:
x → 30
y → 30
Comparison:
30 === 30
↓
true
Tetapi object:
let obj1 = { age: 28 };
let obj2 = { age: 28 };
mental modelnya:
obj1 ─────→ Object A
{ age: 28 }
obj2 ─────→ Object B
{ age: 28 }
Perhatikan:
Object A dan Object B berbeda.
Walaupun isinya sama:
Object A = { age: 28 }
Object B = { age: 28 }
mereka tetap merupakan dua object yang berbeda.
Maka:
obj1 === obj2
hasilnya:
false
Karena reference-nya berbeda.
6. Analogi rumah
Bayangkan ada dua rumah:
Rumah A
Alamat: Jalan Mawar 10
Isi: TV, sofa, meja
dan:
Rumah B
Alamat: Jalan Melati 20
Isi: TV, sofa, meja
Isi rumahnya sama persis.
Apakah itu berarti rumahnya sama?
Tidak.
Alamatnya berbeda.
Begitu juga:
let obj1 = { age: 28 };
let obj2 = { age: 28 };
Secara sederhana:
obj1 → alamat A
obj2 → alamat B
Jadi:
obj1 === obj2
→ false.
7. Lalu bagaimana supaya hasilnya true?
Nah, sekarang:
let obj1 = {
age: 28
};
let obj2 = obj1;
console.log(obj1 === obj2);
Hasil:
true
Kenapa?
Karena:
obj2 = obj1;
tidak membuat object baru.
Yang disalin adalah reference.
Mental model:
obj1 ──┐
↓
Object A
{ age: 28 }
↑
obj2 ──┘
Sekarang obj1 dan obj2 menunjuk ke object yang sama.
Maka:
obj1 === obj2
hasil:
true
8. Ini perbedaan yang sangat penting
Bandingkan dua kode berikut.
Kasus A
let obj1 = {
age: 28
};
let obj2 = {
age: 28
};
Ada dua object.
obj1 → Object A
obj2 → Object B
Maka:
obj1 === obj2
// false
Kasus B
let obj1 = {
age: 28
};
let obj2 = obj1;
Hanya ada satu object.
obj1 ──┐
↓
Object A
↑
obj2 ──┘
Maka:
obj1 === obj2
// true
9. Jangan salah mengartikan obj2 = obj1
Ini bukan:
“Buat object baru berdasarkan obj1.”
Melainkan:
“Salin reference yang dimiliki obj1 ke obj2.”
Jadi:
obj2 = obj1;
lebih tepat dibayangkan sebagai:
obj2 = alamat yang dimiliki obj1
bukan:
obj2 = object baru yang isinya sama
10. Sekarang contoh array
Bagian ini juga sangat penting.
Misalnya:
let arr1 = [10, 20, 30];
let arr2 = arr1;
arr2.push(40);
arr2.push(50);
console.log(arr1);
console.log(arr2);
Mungkin kamu awalnya berpikir:
arr1 = [10, 20, 30]
arr2 = copy arr1
arr2 = [10, 20, 30, 40, 50]
arr1 = [10, 20, 30]
Tetapi hasil sebenarnya:
arr1
// [10, 20, 30, 40, 50]
arr2
// [10, 20, 30, 40, 50]
Kenapa?
Karena Array adalah reference type.
11. Apa yang sebenarnya terjadi?
Awalnya:
let arr1 = [10, 20, 30];
Mental model:
arr1 ───────→ [10, 20, 30]
Kemudian:
let arr2 = arr1;
Yang disalin adalah reference:
arr1 ───────┐
↓
[10, 20, 30]
↑
arr2 ───────┘
Sekarang:
arr1
\
→ array yang sama
/
arr2
Bukan:
arr1 → Array A
arr2 → Array B
12. Lalu push() bekerja pada object yang mana?
Kita melakukan:
arr2.push(40);
arr2 menunjuk ke:
[10, 20, 30]
Maka array tersebut berubah:
[10, 20, 30, 40]
Tetapi arr1 juga menunjuk ke array yang sama.
Jadi:
arr1 ─────┐
↓
[10, 20, 30, 40]
↑
arr2 ─────┘
Kemudian:
arr2.push(50);
menjadi:
arr1 ─────┐
↓
[10, 20, 30, 40, 50]
↑
arr2 ─────┘
Jadi:
console.log(arr1);
// [10, 20, 30, 40, 50]
console.log(arr2);
// [10, 20, 30, 40, 50]
13. Ini disebut mutation
Perhatikan:
arr2.push(40);
Kita tidak mengganti arr2 dengan array baru.
Kita mengubah isi array yang sedang ditunjuk oleh arr2.
Ini disebut:
mutation = mengubah isi object/array yang sudah ada.
Contoh mutation:
arr2.push(40);
obj.age = 30;
Keduanya mengubah object yang sudah ada.
14. Mutation vs reassignment
Ini konsep yang wajib kamu bedakan.
Mutation
obj.age = 30;
Object-nya tetap sama.
obj ───→ Object A
age: 30
Reassignment
obj = {
age: 40
};
Sekarang obj diarahkan ke object lain.
obj ───→ Object B
age: 40
Jadi:
MUTATION
obj.age = 30
↓
ubah isi object
REASSIGNMENT
obj = {...}
↓
ubah object yang ditunjuk variabel
Ini akan sangat berguna ketika nanti belajar function arguments.
15. Contoh reassignment pada array dari lecture
Lecture kemudian melakukan sesuatu seperti:
let arr1 = [10, 20, 30];
let arr2 = arr1;
arr1 = [];
arr2.push(40);
arr2.push(50);
console.log(arr1);
console.log(arr2);
Sekarang hasilnya berbeda.
Mari pelan-pelan.
Awalnya:
arr1 ─────┐
↓
[10,20,30]
↑
arr2 ─────┘
Kemudian:
arr1 = [];
Kita membuat array baru.
Sekarang:
arr1 ─────→ []
arr2 ─────→ [10,20,30]
Jadi arr1 dan arr2 sudah tidak menunjuk ke array yang sama.
Kemudian:
arr2.push(40);
arr2.push(50);
Array yang ditunjuk arr2 menjadi:
arr1 ─────→ []
arr2 ─────→ [10,20,30,40,50]
Maka:
console.log(arr1);
// []
console.log(arr2);
// [10,20,30,40,50]
16. Ini perbedaan mutation dan reassignment lagi
Awalnya:
let arr1 = [10, 20, 30];
let arr2 = arr1;
arr1 ──┐
↓
Array A
↑
arr2 ──┘
Kalau:
arr2.push(40);
yang berubah adalah:
Array A
Sehingga keduanya melihat perubahan:
arr1 ──┐
↓
[10,20,30,40]
↑
arr2 ──┘
Tetapi kalau:
arr1 = [];
yang berubah adalah reference milik arr1:
arr1 ─────→ Array B []
arr2 ─────→ Array A [10,20,30]
Jadi arr1 dan arr2 sudah berpisah.
17. Diagram lengkap
Ini diagram yang menurut saya paling penting dari lecture:
AWAL
arr1 ─────┐
↓
Array A
[10, 20, 30]
↑
arr2 ─────┘
Kemudian:
arr2.push(40);
menjadi:
arr1 ─────┐
↓
Array A
[10,20,30,40]
↑
arr2 ─────┘
Tetapi kalau:
arr1 = [];
menjadi:
arr1 ─────→ Array B
[]
arr2 ─────→ Array A
[10,20,30,40]
Reference-nya sekarang berbeda.
18. Contoh terakhir: primitive di dalam object
Lecture memberikan contoh yang sedikit lebih tricky:
let obj1 = {
age: 28
};
let age = 28;
console.log(age === obj1.age);
Hasil:
true
Ini mungkin membuat bingung:
“Bukankah obj1 adalah reference type?”
Ya, obj1 adalah reference type.
Tetapi kita tidak sedang membandingkan:
obj1 === sesuatu
Kita sedang membandingkan:
age === obj1.age
Ini berbeda.
19. Apa sebenarnya obj1.age?
Object:
let obj1 = {
age: 28
};
Secara sederhana:
obj1
↓
Object
└── age → 28
obj1 adalah reference ke object.
Tetapi property:
obj1.age
menghasilkan nilai:
28
Dan 28 adalah Number, yaitu primitive/value type.
Jadi:
age === obj1.age
sebenarnya menjadi:
28 === 28
Maka:
true
20. Jangan menganggap semua yang ada di object adalah reference
Ini kesalahan yang sering terjadi.
Misalnya:
let person = {
name: "John",
age: 28
};
person adalah object/reference type.
Tetapi property-nya:
person
↓
Object
├── name → "John"
└── age → 28
name berisi String primitive.
age berisi Number primitive.
Jadi:
person.age
menghasilkan:
28
bukan “reference ke object”.
21. Object bisa berisi object juga
Misalnya:
let person = {
name: "John",
address: {
city: "Jakarta"
}
};
Sekarang:
person
↓
Object
├── name → "John"
│
└── address
↓
Object
└── city → "Jakarta"
person adalah reference type.
person.name menghasilkan String.
person.address menghasilkan object/reference.
Jadi jenis data yang diakses tetap penting.
22. Hubungannya dengan memory Stack dan Heap
Lecture menggunakan model:
Stack
↓
Heap
Secara sederhana untuk belajar:
Primitive
let x = 30;
dibayangkan:
Stack
x → 30
Object
let obj = {
age: 28
};
dibayangkan:
Stack Heap
obj ───────────────→ { age: 28 }
reference
Jadi variabel obj secara mental model menyimpan reference menuju object.
Dua variabel
let obj1 = { age: 28 };
let obj2 = obj1;
Stack Heap
obj1 ───────┐
↓
reference ──→ { age: 28 }
↑
obj2 ───────┘
Dua object berbeda
let obj1 = { age: 28 };
let obj2 = { age: 28 };
Stack Heap
obj1 ───────────────→ { age: 28 }
Object A
obj2 ───────────────→ { age: 28 }
Object B
Reference berbeda.
Maka:
obj1 === obj2
→ false.
23. Tetapi ada catatan teknis tentang Stack dan Heap
Untuk level course ini, diagram Stack/Heap sangat bagus sebagai mental model.
Namun jangan menganggap JavaScript specification mengatakan:
“Semua primitive pasti berada di stack dan semua object pasti berada di heap.”
Tidak sesederhana itu.
Engine JavaScript seperti V8 melakukan banyak optimisasi internal.
Jadi yang paling penting untuk kamu ingat bukan lokasi memory secara literal.
Yang lebih penting:
PRIMITIVE
→ value
OBJECT
→ reference ke object
Stack/Heap adalah cara sederhana untuk membantu kita membayangkan konsep tersebut.
24. Kenapa pemahaman ini penting untuk debugging?
Bayangkan kamu punya:
let user1 = {
name: "John",
age: 28
};
let user2 = user1;
Kemudian:
user2.age = 30;
Kamu mungkin berpikir:
“Saya hanya mengubah user2.”
Tetapi sebenarnya:
user1 ──┐
↓
SAME OBJECT
↑
user2 ──┘
Jadi:
console.log(user1.age);
hasilnya:
30
Bukan:
28
Ini bisa menyebabkan bug jika kamu tidak sadar bahwa dua variabel menunjuk object yang sama.
25. Bagaimana membuat copy array yang benar?
Lecture ini menunjukkan bahwa:
let arr2 = arr1;
bukan copy array.
Kalau kita ingin membuat array baru, salah satu cara modern:
let arr1 = [10, 20, 30];
let arr2 = [...arr1];
arr2.push(40);
console.log(arr1);
// [10, 20, 30]
console.log(arr2);
// [10, 20, 30, 40]
Sekarang:
arr1 ───→ Array A
[10,20,30]
arr2 ───→ Array B
[10,20,30,40]
Array-nya berbeda.
Catatan: ... menghasilkan shallow copy, jadi kalau array/object-nya memiliki object bersarang, ceritanya bisa lebih dalam. Itu biasanya dipelajari setelah konsep dasar ini kuat.
26. Hal yang sangat sering menjebak pemula
Perhatikan:
let a = {
name: "John"
};
let b = {
name: "John"
};
console.log(a === b);
Hasil:
false
Walaupun:
a.name === b.name
adalah:
true
Karena:
a === b
membandingkan object/reference.
Sedangkan:
a.name === b.name
membandingkan String/value.
27. Ini bisa dibuat tabel
| Expression | Yang dibandingkan | Hasil |
|---|---|---|
30 === 30 | value | true |
"John" === "John" | value | true |
true === true | value | true |
{age:28} === {age:28} | reference | false |
obj1 === obj2 jika obj2 = obj1 | reference yang sama | true |
arr1 === arr2 jika arr2 = arr1 | reference yang sama | true |
[1,2] === [1,2] | reference berbeda | false |
obj1.age === obj2.age | value 28 vs 28 | true |
28. Satu mental model yang harus benar-benar kamu kuasai
Kalau melihat:
let x = 10;
let y = x;
pikir:
x → 10
y → 10
copy value.
Tetapi kalau:
let x = { age: 10 };
let y = x;
pikir:
x ──┐
↓
Object
↑
y ──┘
copy reference.
29. Cara membaca assignment object
Mulai sekarang kalau kamu melihat:
let b = a;
tanyakan dulu:
“Apa isi
a?”
Kalau a primitive:
let a = 10;
let b = a;
→ copy value.
a → 10
b → 10
Kalau a object:
let a = { x: 10 };
let b = a;
→ copy reference.
a ──┐
↓
Object
↑
b ──┘
Ini cara berpikir yang sangat berguna.
30. Hubungan dengan lecture sebelumnya tentang arguments
Kita baru saja belajar:
arguments
dan sekarang belajar:
value vs reference
Keduanya akan sangat berkaitan dengan lecture berikutnya tentang pass by value dan pass by reference.
Misalnya:
function change(user) {
user.age = 30;
}
let person = {
age: 28
};
change(person);
console.log(person.age);
Hasil:
30
Kenapa?
Karena ketika object diberikan ke function, behavior reference-nya ikut berperan.
Nanti konsep ini akan dibahas lebih detail di lecture pass by value/pass by reference.
31. Catatan teknis penting: JavaScript sebenarnya pass-by-value
Ini mungkin akan muncul lagi di lecture berikutnya.
Banyak tutorial mengatakan:
“Primitive pass by value, object pass by reference.”
Sebagai penyederhanaan untuk pemula, itu sering dipakai.
Tetapi secara teknis, JavaScript lebih tepat dikatakan:
JavaScript selalu pass-by-value.
Untuk object, value yang disalin adalah reference ke object tersebut.
Misalnya:
function change(obj) {
obj.age = 30;
}
Reference tersebut disalin ke parameter obj, tetapi kedua reference mengarah ke object yang sama.
Jadi:
person ─────┐
↓
Object A
↑
└──── obj
Maka obj.age = 30 memodifikasi Object A.
Ini akan menjadi sangat penting di lecture berikutnya.
32. Ringkasan seluruh lecture
Secara keseluruhan:
JavaScript Values
│
┌─────────┴─────────┐
↓ ↓
Primitive Object
/ Value Type / Reference Type
│ │
copy VALUE copy REFERENCE
│ │
30 → 30 obj → alamat
│ │
independent bisa menunjuk
object yang sama
Contoh:
Value type
let a = 10;
let b = a;
a = 20;
console.log(a); // 20
console.log(b); // 10
Karena:
a → 20
b → 10
Reference type
let a = {
value: 10
};
let b = a;
b.value = 20;
console.log(a.value); // 20
console.log(b.value); // 20
Karena:
a ──┐
↓
Object
value: 20
↑
b ──┘
33. Cheat sheet 🔥
Simpan pola ini:
PRIMITIVE
Number
String
Boolean
Undefined
Null
BigInt
Symbol
↓
VALUE TYPE
↓
copy value
Sedangkan:
Object
Array
Function
↓
REFERENCE TYPE
↓
copy reference
Dan yang paling penting:
let a = 10;
let b = a;
a = 20;
a !== b
karena value disalin.
Sedangkan:
let a = {};
let b = a;
a === b
karena reference yang sama.
34. Tiga pertanyaan untuk membongkar soal seperti ini
Kalau nanti kamu melihat code yang membingungkan, lakukan 3 langkah:
Pertanyaan 1 — Ini primitive atau object?
30
→ primitive.
{}
→ object.
[]
→ object/reference type.
Pertanyaan 2 — Assignment ini membuat copy value atau copy reference?
let b = a;
Kalau a primitive:
copy value
Kalau a object:
copy reference
Pertanyaan 3 — Saya sedang mengubah variable atau object?
b = {};
→ reassignment
Sedangkan:
b.age = 30;
→ mutation
Ini dua hal yang berbeda.
🧠 Inti yang harus kamu ingat
Kalau saya harus merangkum lecture ini menjadi satu kalimat:
Primitive ketika di-assignment akan menyalin value-nya, sedangkan object/array ketika di-assignment akan menyalin reference-nya.
Dan untuk comparison:
30 === 30
// true
karena yang dibandingkan adalah value.
Sedangkan:
{} === {}
// false
karena dua {} menghasilkan dua object berbeda.
Tetapi:
const a = {};
const b = a;
a === b
// true
karena a dan b menunjuk object yang sama.
Kalau konsep “fotokopi vs alamat rumah” ini sudah benar-benar masuk, kamu akan jauh lebih mudah memahami lecture berikutnya tentang pass by value dan pass by reference.