Obyektlar: asoslar

Obyekt havolalari va nusxalash

Obyektlar va primitivlar o'rtasidagi eng muhim farqlardan biri โ€” ular saqlanish va ko'chirilish usulida. Primitivlar to'liq qiymat bilan, obyektlar esa havola (reference) bilan ishlaydi. Bu farqni tushunmaslik ko'plab "g'alati" xatolarga sabab bo'ladi.

Primitivlar nusxalanadi

Primitiv turdagi o'zgaruvchi (satr, son, boolean) o'z qiymatini to'liq saqlaydi. Uni boshqa o'zgaruvchiga tenglashtirsak, qiymatning nusxasi ko'chiriladi โ€” ikki mustaqil qiymat hosil bo'ladi:

primitiv.js
// natija shu yerda chiqadi

Obyektlar havola orqali saqlanadi

Obyekt o'zgaruvchisi obyektning o'zini emas, balki uning xotiradagi manzilini โ€” ya'ni "havola"ni saqlaydi. Buni quyidagicha tasavvur qiling: obyekt โ€” bir qutida, o'zgaruvchi esa โ€” o'sha qutiga yopishtirilgan qog'oz (manzil, kalit).

let user = {
  name: "Jaloliddin"
};
// user o'zgaruvchisi obyektning "manzilini" saqlaydi,
// obyektning o'zini emas.

Xossaga murojaat qilganda (user.name), JavaScript avval havola bo'yicha obyektni topadi, keyin uning ichidagi xossani o'qiydi.

Havolani nusxalash

Obyekt o'zgaruvchisini nusxalasak, obyekt EMAS, balki havola nusxalanadi. Natijada ikkala o'zgaruvchi ham bitta va o'sha obyektga ishora qiladi:

havola.js
// natija shu yerda chiqadi

Bu "ikkita kaliti bor bitta shkaf"ga o'xshaydi: bittasi bilan ochib narsa qo'ysak, ikkinchi kalit egasi ham o'sha narsani ko'radi. Obyekt bitta, unga ikkita havola bor.

Bu โ€” eng ko'p yangi xatoga sabab bo'ladigan nuqtalardan biri. let b = a obyektni nusxalamaydi; u faqat yana bitta havola yaratadi.

Havola orqali taqqoslash

== va === operatorlari ikkita obyekt uchun bir xil ishlaydi: ular faqat ikkala havola bitta va o'sha obyektga ishora qilsagina true qaytaradi:

tenglik1.js
// natija shu yerda chiqadi

Ammo tashqi ko'rinishi bir xil, ammo mustaqil ikkita obyekt HAR DOIM teng emas:

tenglik2.js
// natija shu yerda chiqadi
Ular ko'rinishi "bo'sh va bir xil" bo'lsa-da, xotirada ikki alohida obyekt โ€” shuning uchun teng emas.

Object.assign bilan nusxalash

Chinakam nusxa โ€” mustaqil, alohida obyekt โ€” kerak bo'lsa-chi? Buning uchun barcha xossalarni yangi obyektga ko'chirish kerak. Buni qo'lda for..in bilan qilsa bo'ladi, ammo tayyor usul bor: Object.assign.

Sintaksis: Object.assign(dest, ...sources). U sourcesdagi barcha xossalarni destga ko'chiradi va destni qaytaradi:

assign.js
// natija shu yerda chiqadi

Object.assign bir nechta manbani ham birlashtira oladi. Bir xil nomli xossa bo'lsa, oxirgisi ustun keladi:

assign-merge.js
// natija shu yerda chiqadi
Nusxa olishning yana bir keng tarqalgan usuli โ€” spread sintaksisi: let clone = {...user};. U ham xuddi shunday sayoz nusxa yaratadi.

Sayoz nusxa (shallow copy) muammosi

Object.assign (va spread) faqat bir daraja chuqurlikda nusxalaydi. Agar xossa qiymati o'zi obyekt bo'lsa, u nusxalanmaydi โ€” uning havolasi ko'chiriladi. Bunga sayoz nusxa (shallow copy) deyiladi:

sayoz.js
// natija shu yerda chiqadi
Sayoz nusxada tashqi obyekt yangi, ammo ichki obyektlar hamon ulashilgan. Bittasini o'zgartirsak, ikkinchisi ham o'zgaradi.

Chuqur nusxa: structuredClone

Bunday muammoni bartaraf qilish uchun chuqur nusxa (deep clone) kerak โ€” barcha darajadagi ichki obyektlar ham nusxalanadi. Zamonaviy JavaScript'da buning uchun ichki structuredClone funksiyasi bor:

structured.js
// natija shu yerda chiqadi

structuredClone istalgan chuqurlikdagi obyektlarni, hatto o'z-o'ziga ishora qiluvchi (siklik) obyektlarni ham to'g'ri nusxalay oladi.

structuredClone funksiya (metod) qiymatlarini nusxalay olmaydi โ€” bunday obyektni uzatsangiz xato beradi. Bunday murakkab hollarda tashqi kutubxonalar (masalan, lodash'ning _.cloneDeep) yordam beradi.

Xulosa