Obyektlar va primitivlar oârtasidagi asosiy farqlardan biri shundaki, obyektlar âhavola boâyichaâ saqlanadi va nusxalanadi, primitivlar esa: satrlar, raqamlar, boolean va hokazo â har doim âtoâliq qiymatâ sifatida nusxalanadi.
Qiymat nusxalanganda nima sodir boâlishini biroz chuqurroq koârsak, buni tushunish oson.
Satr kabi primitiv bilan boshlaylik.
Bu yerda biz message ning nusxasini phrase ga qoâyamiz:
let message = "Salom!";
let phrase = message;
Natijada bizda ikkita mustaqil oâzgaruvchi bor, har biri "Salom!" satrini saqlaydi.
Aniq natija, toâgârimi?
Obyektlar bunday emas.
Obyektga tayinlangan oâzgaruvchi obyektning oâzini emas, balki uning âxotiradagi manziliniâ â boshqacha qilib aytganda, unga âhavolaniâ saqlaydi.
Bunday oâzgaruvchining misolini koâramiz:
let user = {
name: "John",
};
Va u xotirada qanday saqlanishi:
Obyekt xotirada biror joyda saqlanadi (rasmning oâng tomonida), user oâzgaruvchisi (chap tomonda) esa unga âhavolaâ qiladi.
user kabi obyekt oâzgaruvchisini obyektning manzili yozilgan qogâoz varaq deb tasavvur qilishimiz mumkin.
Obyekt bilan amallarni bajarganimizda, masalan user.name xossasini olganimizda, JavaScript dvigateli oâsha manzilda nima borligini koâradi va haqiqiy obyektda operatsiyani bajaradi.
Endi bu nima uchun muhimligi.
Obyekt oâzgaruvchisi nusxalanganda, havola nusxalanadi, lekin obyektning oâzi takrorlanmaydi.
Masalan:
let user = { name: "John" };
let admin = user; // havolani nusxalash
Endi bizda ikkita oâzgaruvchi bor, har biri bir xil obyektga havola qiladi:
Koârib turganingizdek, hali ham bitta obyekt bor, lekin endi unga havola qiluvchi ikkita oâzgaruvchi bor.
Obyektga kirish va uning tarkibini oâzgartirish uchun har qanday oâzgaruvchidan foydalanishimiz mumkin:
let user = { name: 'John' };
let admin = user;
admin.name = 'Pete'; // "admin" havolasi orqali o'zgartirildi
alert(user.name); // 'Pete', o'zgarishlar "user" havolasidan ko'rinadi
Bu xuddi bizda ikkita kaliti boâlgan shkaf boâlgandek va ulardan birini (admin) ishlatib unga kirib oâzgarishlar qildik. Keyin, agar boshqa kalitni (user) ishlatsak, biz hali ham bir xil shkafdamiz va oâzgartirilgan tarkibga kira olamiz.
Havola boâyicha solishtirish
Ikki obyekt faqat bir xil obyekt boâlsagina teng hisoblanadi.
Masalan, bu yerda a va b bir xil obyektga havola qiladi, shuning uchun ular teng:
let a = {};
let b = a; // havolani nusxalash
alert(a == b); // true, ikkala o'zgaruvchi bir xil obyektga havola qiladi
alert(a === b); // true
Va bu yerda ikkita mustaqil obyekt teng emas, garchi ular oâxshash koârinsa ham (ikkalasi ham boâsh):
let a = {};
let b = {}; // ikkita mustaqil obyekt
alert(a == b); // false
obj1 > obj2 kabi solishtirishlar uchun yoki primitiv bilan solishtirish obj == 5 uchun, obyektlar primitivlarga aylantiriladi. Obyekt aylantirishlari qanday ishlashini tez orada oârganamiz, lekin rostini aytganda, bunday solishtirishlar juda kam kerak boâladi â odatda ular dasturlash xatosi natijasida paydo boâladi.
Obyektlarni havola sifatida saqlashning muhim yon taâsiri shundaki, const sifatida eâlon qilingan obyektni oâzgartirish mumkin.
Masalan:
const user = {
name: "John"
};
user.name = "Pete"; // (*)
alert(user.name); // Pete
(*) qatori xato keltirib chiqarishi kerak boâlgandek tuyuladi, lekin bunday emas. user qiymati doimiy, u har doim bir xil obyektga havola qilishi kerak, lekin oâsha obyektning xossalari erkin oâzgarishi mumkin.
Boshqacha qilib aytganda, const user faqat biz user=... ni butunlay oârnatishga harakat qilganimizda xato beradi.
Shuni aytish kerakki, agar biz haqiqatan ham doimiy obyekt xossalarini yaratishimiz kerak boâlsa, bu ham mumkin, lekin butunlay boshqa usullardan foydalanib. Buni Xususiyat bayroqlari va tavsiflovchilar bobida eslatamiz.
Klonlash va birlashtirish, Object.assign
Shunday qilib, obyekt oâzgaruvchisini nusxalash bir xil obyektga yana bir havola yaratadi.
Lekin agar bizga obyektni takrorlash kerak boâlsa-chi?
Biz yangi obyekt yaratishimiz va mavjud obyektning tuzilmasini takrorlashimiz mumkin, uning xossalari boâylab iteratsiya qilib va ularni primitiv darajada nusxalash orqali.
Quyidagicha:
let user = {
name: "John",
age: 30
};
let clone = {}; // yangi bo'sh obyekt
// keling, user ning barcha xossalarini unga nusxalaylik
for (let key in user) {
clone[key] = user[key];
}
// endi clone bir xil tarkibga ega to'liq mustaqil obyekt
clone.name = "Pete"; // undagi ma'lumotni o'zgartirdik
alert( user.name ); // asl obyektda hali ham John
Shuningdek, biz Object.assign usulidan ham foydalanishimiz mumkin.
Sintaksis:
Object.assign(dest, ...sources);
- Birinchi argument
destâ maqsadli obyekt. - Keyingi argumentlar â manba obyektlar roâyxati.
U barcha manba obyektlarning xossalarini maqsadli dest ga nusxalaydi va keyin natija sifatida uni qaytaradi.
Masalan, bizda user obyekt bor, unga bir nechta ruxsatlar qoâshamiz:
let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
// permissions1 va permissions2 ning barcha xossalarini user ga nusxalaydi
Object.assign(user, permissions1, permissions2);
// endi user = { name: "John", canView: true, canEdit: true }
alert(user.name); // John
alert(user.canView); // true
alert(user.canEdit); // true
Agar nusxalangan xossa nomi allaqachon mavjud boâlsa, u qayta yoziladi:
let user = { name: "John" };
Object.assign(user, { name: "Pete" });
alert(user.name); // endi user = { name: "Pete" }
Shuningdek, biz Object.assign dan oddiy obyekt klonlash uchun ham foydalanishimiz mumkin:
let user = {
name: "John",
age: 30
};
let clone = Object.assign({}, user);
alert(clone.name); // John
alert(clone.age); // 30
Bu yerda u user ning barcha xossalarini boâsh obyektga nusxalaydi va uni qaytaradi.
Obyektni klonlashning boshqa usullari ham bor, masalan spread sintaksisi clone = {...user} dan foydalanish, oâquv qoâllanmasida keyinroq yoritilgan.
Ichma-ich klonlash
Hozirgacha biz user ning barcha xossalari primitiv deb faraz qildik. Lekin xossalar boshqa obyektlarga havolalar boâlishi mumkin.
Quyidagicha:
let user = {
name: "John",
sizes: {
height: 182,
width: 50,
},
};
alert(user.sizes.height); // 182
Endi clone.sizes = user.sizes ni nusxalash yetarli emas, chunki user.sizes obyekt va havola boâyicha nusxalanadi, shuning uchun clone va user bir xil sizes ni boâlishadi:
let user = {
name: "John",
sizes: {
height: 182,
width: 50,
},
};
let clone = Object.assign({}, user);
alert(user.sizes === clone.sizes); // true, bir xil obyekt
// user va clone sizes ni bo'lishadi
user.sizes.width = 60; // bir joydan xossani o'zgartirish
alert(clone.sizes.width); // 60, natijani boshqa joydan olish
Buni tuzatish va user bilan clone ni haqiqatan ham alohida obyektlar qilish uchun biz user[key] ning har bir qiymatini tekshiradigan va agar u obyekt boâlsa, uning tuzilmasini ham takrorlaydigan klonlash tsiklidan foydalanishimiz kerak. Bu âchuqur klonlashâ yoki âtuzilmaviy klonlashâ deb ataladi. Chuqur klonlashni amalga oshiradigan structuredClone usuli mavjud.
structuredClone
structuredClone(object) chaqiruvi object ni barcha ichma-ich xossalar bilan klonlaydi.
Misolimizda qanday foydalanishni koâramiz:
let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
let clone = structuredClone(user);
alert( user.sizes === clone.sizes ); // false, turli obyektlar
// user va clone endi butunlay bog'liq emas
user.sizes.width = 60; // bir joydan xossani o'zgartirish
alert(clone.sizes.width); // 50, bog'liq emas
structuredClone usuli obyektlar, massivlar, primitiv qiymatlar kabi koâpchilik maâlumot turlarini klonlay oladi.
U shuningdek obyekt xossasi obyektning oâziga havola qilgan (bevosita yoki havolalar zanjiri orqali) aylanma havolalarni ham qoâllab-quvvatlaydi.
Masalan:
let user = {};
// keling, aylanma havola yarataylik:
// user.me user ning o'ziga havola qiladi
user.me = user;
let clone = structuredClone(user);
alert(clone.me === clone); // true
Koârib turganingizdek, clone.me user ga emas, clone ga havola qiladi! Shunday qilib, aylanma havola ham toâgâri klonlandi.
Garchi, structuredClone muvaffaqiyatsiz boâlgan holatlar ham bor.
Masalan, obyektda funksiya xossasi boâlganda:
// xato
structuredClone({
f: function () {},
});
Funksiya xossalari qoâllab-quvvatlanmaydi.
Bunday murakkab holatlarni hal qilish uchun bizga klonlash usullarining kombinatsiyasidan foydalanish, maxsus kod yozish yoki gâildirakni qayta ixtiro qilmaslik uchun mavjud implementatsiyani olish kerak boâlishi mumkin, masalan lodash JavaScript kutubxonasidagi _.cloneDeep(obj).
Xulosa
Obyektlar havola boâyicha tayinlanadi va nusxalanadi. Boshqacha qilib aytganda, oâzgaruvchi âobyekt qiymatiâ ni emas, balki qiymat uchun âhavolaâ (xotiradagi manzil) ni saqlaydi. Shunday qilib, bunday oâzgaruvchini nusxalash yoki uni funksiya argumenti sifatida uzatish obyektning oâzini emas, oâsha havolani nusxalaydi.
Nusxalangan havolalar orqali barcha amallar (xossalar qoâshish/olib tashlash kabi) bir xil obyektda amalga oshiriladi.
âHaqiqiy nusxaâ (klon) yaratish uchun biz âsayoz nusxaâ (ichma-ich obyektlar havola boâyicha nusxalanadi) uchun Object.assign dan yoki âchuqur klonlashâ funksiyasi structuredClone dan yoki _.cloneDeep(obj) kabi maxsus klonlash implementatsiyasidan foydalanishimiz mumkin.
Izohlar
<code>yorlig'ini ishlating, bir nechta satrlar uchun - ularni<pre>yorlig'i bilan o'rab qo'ying, 10 satrdan ortiq bo'lsa - sandbox (plnkr, jsbin, codepenâ¦)