Obyektlar va primitivlar ortasidagi asosiy farqlardan biri shundaki, obyektlar havola boyicha saqlanadi va nusxalanadi, primitivlar esa: satrlar, raqamlar, boolean va hokazo har doim toliq qiymat sifatida nusxalanadi.
Qiymat nusxalanganda nima sodir bolishini biroz chuqurroq korsak, buni tushunish oson.
Satr kabi primitiv bilan boshlaylik.
Bu yerda biz message ning nusxasini phrase ga qoyamiz:
let message = "Salom!";
let phrase = message;
Natijada bizda ikkita mustaqil ozgaruvchi bor, har biri "Salom!" satrini saqlaydi.
Aniq natija, togrimi?
Obyektlar bunday emas.
Obyektga tayinlangan ozgaruvchi obyektning ozini emas, balki uning xotiradagi manzilini boshqacha qilib aytganda, unga havolani saqlaydi.
Bunday ozgaruvchining misolini koramiz:
let user = {
name: "John",
};
Va u xotirada qanday saqlanishi:
Obyekt xotirada biror joyda saqlanadi (rasmning ong tomonida), user ozgaruvchisi (chap tomonda) esa unga havola qiladi.
user kabi obyekt ozgaruvchisini obyektning manzili yozilgan qogoz varaq deb tasavvur qilishimiz mumkin.
Obyekt bilan amallarni bajarganimizda, masalan user.name xossasini olganimizda, JavaScript dvigateli osha manzilda nima borligini koradi va haqiqiy obyektda operatsiyani bajaradi.
Endi bu nima uchun muhimligi.
Obyekt ozgaruvchisi nusxalanganda, havola nusxalanadi, lekin obyektning ozi takrorlanmaydi.
Masalan:
let user = { name: "John" };
let admin = user; // havolani nusxalash
Endi bizda ikkita ozgaruvchi bor, har biri bir xil obyektga havola qiladi:
Korib turganingizdek, hali ham bitta obyekt bor, lekin endi unga havola qiluvchi ikkita ozgaruvchi bor.
Obyektga kirish va uning tarkibini ozgartirish uchun har qanday ozgaruvchidan 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 bolgan shkaf bolgandek va ulardan birini (admin) ishlatib unga kirib ozgarishlar qildik. Keyin, agar boshqa kalitni (user) ishlatsak, biz hali ham bir xil shkafdamiz va ozgartirilgan tarkibga kira olamiz.
Havola boyicha solishtirish
Ikki obyekt faqat bir xil obyekt bolsagina 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 oxshash korinsa ham (ikkalasi ham bosh):
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 organamiz, lekin rostini aytganda, bunday solishtirishlar juda kam kerak boladi odatda ular dasturlash xatosi natijasida paydo boladi.
Obyektlarni havola sifatida saqlashning muhim yon tasiri shundaki, const sifatida elon qilingan obyektni ozgartirish mumkin.
Masalan:
const user = {
name: "John"
};
user.name = "Pete"; // (*)
alert(user.name); // Pete
(*) qatori xato keltirib chiqarishi kerak bolgandek tuyuladi, lekin bunday emas. user qiymati doimiy, u har doim bir xil obyektga havola qilishi kerak, lekin osha obyektning xossalari erkin ozgarishi mumkin.
Boshqacha qilib aytganda, const user faqat biz user=... ni butunlay ornatishga harakat qilganimizda xato beradi.
Shuni aytish kerakki, agar biz haqiqatan ham doimiy obyekt xossalarini yaratishimiz kerak bolsa, bu ham mumkin, lekin butunlay boshqa usullardan foydalanib. Buni Xususiyat bayroqlari va tavsiflovchilar bobida eslatamiz.
Klonlash va birlashtirish, Object.assign
Shunday qilib, obyekt ozgaruvchisini nusxalash bir xil obyektga yana bir havola yaratadi.
Lekin agar bizga obyektni takrorlash kerak bolsa-chi?
Biz yangi obyekt yaratishimiz va mavjud obyektning tuzilmasini takrorlashimiz mumkin, uning xossalari boylab 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
destmaqsadli obyekt. - Keyingi argumentlar manba obyektlar royxati.
U barcha manba obyektlarning xossalarini maqsadli dest ga nusxalaydi va keyin natija sifatida uni qaytaradi.
Masalan, bizda user obyekt bor, unga bir nechta ruxsatlar qoshamiz:
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 bolsa, 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 bosh obyektga nusxalaydi va uni qaytaradi.
Obyektni klonlashning boshqa usullari ham bor, masalan spread sintaksisi clone = {...user} dan foydalanish, oquv qollanmasida keyinroq yoritilgan.
Ichma-ich klonlash
Hozirgacha biz user ning barcha xossalari primitiv deb faraz qildik. Lekin xossalar boshqa obyektlarga havolalar bolishi 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 boyicha nusxalanadi, shuning uchun clone va user bir xil sizes ni bolishadi:
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 bolsa, 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 koramiz:
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 kopchilik malumot turlarini klonlay oladi.
U shuningdek obyekt xossasi obyektning oziga havola qilgan (bevosita yoki havolalar zanjiri orqali) aylanma havolalarni ham qollab-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
Korib turganingizdek, clone.me user ga emas, clone ga havola qiladi! Shunday qilib, aylanma havola ham togri klonlandi.
Garchi, structuredClone muvaffaqiyatsiz bolgan holatlar ham bor.
Masalan, obyektda funksiya xossasi bolganda:
// xato
structuredClone({
f: function () {},
});
Funksiya xossalari qollab-quvvatlanmaydi.
Bunday murakkab holatlarni hal qilish uchun bizga klonlash usullarining kombinatsiyasidan foydalanish, maxsus kod yozish yoki gildirakni qayta ixtiro qilmaslik uchun mavjud implementatsiyani olish kerak bolishi mumkin, masalan lodash JavaScript kutubxonasidagi _.cloneDeep(obj).
Xulosa
Obyektlar havola boyicha tayinlanadi va nusxalanadi. Boshqacha qilib aytganda, ozgaruvchi obyekt qiymati ni emas, balki qiymat uchun havola (xotiradagi manzil) ni saqlaydi. Shunday qilib, bunday ozgaruvchini nusxalash yoki uni funksiya argumenti sifatida uzatish obyektning ozini emas, osha havolani nusxalaydi.
Nusxalangan havolalar orqali barcha amallar (xossalar qoshish/olib tashlash kabi) bir xil obyektda amalga oshiriladi.
Haqiqiy nusxa (klon) yaratish uchun biz sayoz nusxa (ichma-ich obyektlar havola boyicha 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)