Ob'ektlar havolalar orqali nusxalanishi(Object references and copying)

Obyektlar va primitivlar o'rtasidagi asosiy farqlardan biri shundaki, obyektlar "havola orqali" saqlanadi va nusxalanadi, primitiv qiymatlar esa satrlar, raqamlar, booleanlar va hokazolar "to'liq qiymat sifatida" har doim nusxalanadi.

Ob'ektlar havolalar orqali nusxalanishi

Obyektlar va primitivlar o'rtasidagi asosiy farqlardan biri shundaki, obyektlar "havola orqali" saqlanadi va nusxalanadi, primitiv qiymatlar esa: satrlar, raqamlar, bo'leanslar va hokazolar "to'liq qiymat sifatida" har doim nusxalanadi.

Bu qiymat nusxalanganda nima sodir bo'lishini bilib olish orqali oson tushuniladi.

Keling, satr kabi bir primitivdan boshlaymiz.

Mana, biz xabarni satrga nusxaladik:

let message = 'Hello!';
let phrase = message;

Natijada, bizda ikkita mustaqil o'zgaruvchi mavjud bo'ladi, ularning har biri "Hello!" satrini saqlaydi.

Object variable copy value

Bu juda oddiy natija, to'g'rimi?

Ob'ektlar bunga o'xshamaydi.

Ob'ektga tayinlangan o'zgaruvchi ob'ektning o'zini emas, balki uning "xotiradagi manzili"ni - boshqacha qilib aytganda, unga "havola" saqlaydi.

Keling, bunday o'zgaruvchining misoliga qaraylik:

let user = {
  name: 'John',
};

Mana, bu qanday qilib xotirada saqlanadi:

Object variable contains reference

Ob'ekt xotiraning biror joyida saqlanadi (rasmning o'ng tomonida), foydalanuvchi o'zgaruvchisi esa unga "havola" saqlaydi.

Biz foydalanuvchi kabi ob'ekt o'zgaruvchisini ob'ektning manzili yozilgan qog'oz varag'i sifatida tasavvur qilishimiz mumkin.

Ob'ekt bilan harakatlar bajarganimizda, masalan, user.name xususiyatini olganda, JavaScript dvigateli manzilda nima borligini tekshiradi va haqiqiy ob'ekt ustida operatsiyani bajaradi.

Mana, nima uchun bu muhim.

Ob'ekt o'zgaruvchisi nusxalanganda, havola nusxalanadi, ammo ob'ektning o'zi dublikat qilinmaydi.

Misol uchun:

let user = { name: 'John' };
 
let admin = user; // havolani nusxalaymiz

Endi bizda ikkita o'zgaruvchi mavjud, ularning har biri bir xil ob'ektga havola saqlaydi:

Object variable copy reference

Ko'rib turganingizdek, bitta ob'ekt bor, ammo endi unga ikkita o'zgaruvchi havola qiladi.

Biz ob'ektga kirish va uning mazmunini 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" havolasi orqali ko'rinadi

Bu go'yo ikkita kalitli shkaf bor va ulardan birini (admin) ishlatib, ichiga kirib o'zgartirishlar kiritamiz. Keyin, agar boshqa kalitni (user) ishlatadigan bo'lsak, biz hali ham bir xil shkafni ochamiz va o'zgartirilgan mazmunga kirishimiz mumkin.

Havola orqali taqqoslash

Ikki ob'ekt faqat ular bir xil ob'ekt bo'lsa teng bo'ladi.

Masalan, bu yerda a va b bir xil ob'ektga havola qiladi, shuning uchun ular teng:

let a = {};
let b = a; // havolani nusxalaymiz
 
alert(a == b); // true, ikkala o'zgaruvchi bir xil ob'ektga havola qiladi
alert(a === b); // true

Va bu yerda ikkita mustaqil ob'ekt teng emas, garchi ular bir-biriga o'xshasa ham (ikkalasi ham bo'sh):

let a = {};
let b = {}; // ikkita mustaqil ob'ekt
 
alert(a == b); // false

Ob'ektlar bilan obj1 > obj2 yoki primitiv bilan taqqoslash obj == 5 kabi taqqoslashlarda ob'ektlar primitivlarga aylantiriladi. Bunday taqqoslashlar juda kam hollarda kerak bo'ladi, chunki odatda ular dasturlashdagi xatolik natijasida paydo bo'ladi.

Const ob'ektlarini o'zgartirish mumkin

Ob'ektlarni havola sifatida saqlashning muhim yon ta'siri shundaki, const deb e'lon qilingan ob'ektni o'zgartirish mumkin.

Masalan:

const user = {
  name: 'John',
};
 
user.name = 'Pete'; // (*)
 
alert(user.name); // Pete

(*): chiziq xatoga olib keladi deb o'ylash mumkin, lekin u xato bermaydi. user qiymati doimiy, u har doim bir xil ob'ektga havola qilishi kerak, ammo ob'ektning xususiyatlari o'zgarishi mumkin.

Boshqacha qilib aytganda, const user faqat user = ... ga butunlay o'rnatishga harakat qilganda xatolik beradi.

Agar biz chindan ham doimiy ob'ekt xususiyatlarini yaratishimiz kerak bo'lsa, bu ham mumkin, ammo butunlay boshqa usullardan foydalangan holda. Bu haqda "Property flags and descriptors" bo'limida gaplashamiz.

Klonlash va birlashtirish, Object.assign

Shunday qilib, ob'ekt o'zgaruvchisini nusxalash yana bir havolani yaratadi.

Ammo agar biz ob'ektni nusxalashimiz kerak bo'lsa-chi?

Biz yangi ob'ekt yaratishimiz va mavjud ob'ektning tuzilishini takrorlashimiz mumkin, uni primitiv darajada nusxalash orqali.

Bu kabi:

let user = {
  name: 'John',
  age: 30,
};
 
let clone = {}; // yangi bo'sh ob'ekt
 
// barcha user xususiyatlarini unga nusxalaymiz
for (let key in user) {
  clone[key] = user[key];
}
 
// endi clone to'liq mustaqil ob'ekt bo'lib, bir xil mazmunli
clone.name = 'Pete'; // undagi ma'lumotlar o'zgartirildi
 
alert(user.name); // hali ham asl ob'ektda "John"

Biz Object.assign metodidan ham foydalanishimiz mumkin.

Sintaksis:

Object.assign(dest, ...sources);

Birinchi argument dest - bu maqsadli ob'ekt. Keyingi argumentlar esa manba ob'ektlarning ro'yxatidir. U barcha manba ob'ektlarining xususiyatlarini maqsadli dest ob'ektiga nusxalaydi, so'ngra natijani qaytaradi.

Misol uchun, bizda user ob'ekti mavjud, keling, unga bir nechta ruxsatlarni qo'shamiz:

let user = { name: 'John' };
 
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
 
// permissions1 va permissions2 barcha xususiyatlarini userga 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 xususiyat nomi allaqachon mavjud bo'lsa, u ustiga yoziladi:

let user = { name: 'John' };
 
Object.assign(user, { name: 'Pete' });
 
alert(user.name); // endi user = { name: "Pete" }

Biz Object.assign metodidan oddiy ob'ektni 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 ob'ektining barcha xususiyatlarini bo'sh ob'ektga nusxalaydi va uni qaytaradi.

Shuningdek, klonlashning boshqa usullari ham mavjud, masalan, tarqalish sintaksisidan foydalanish clone = {...user}, bu keyinchalik qo'llanma davomida o'rganiladi.

Ichki klonlash

Hozirgacha biz user ning barcha xususiyatlari primitiv deb taxmin qildik. Ammo xususiyatlar boshqa ob'ektlarga havola bo'lishi mumkin.

Bu kabi:

let user = {
  name: 'John',
  sizes: {
    height: 182,
    width: 50,
  },
};
 
alert(user.sizes.height); // 182

Endi user.sizes - bu o'zining height va width xususiyatlariga ega bo'lgan ob'ekt. user ning boshqa xususiyatlari kabi, u havola orqali nusxalanadi.

Misol uchun:

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 bir xil `sizes` obyektiga havola qiladi
user.sizes.width++; // width qiymatini o'zgartiramiz havola orqali
alert(clone.sizes.width); // 51, clone.sizes orqali o'zgarishlarni ko'ramiz

Shuning uchun, chuqur klonlashni amalga oshirish uchun rekursiya kerak bo'ladi. Manzillarni saqlaydigan user o'zgarmasligi kerak.

Chuqur klonlash uchun rekursiyani qo'llashni biz keyingi safar ko'rib chiqamiz.

Xulosa

  • Primitivlar bilan o'zgaruvchiga boshqa primitivni tayinlash o'zgaruvchining yangi nusxasini yaratadi.
  • Obyektlar boshqa o'zgaruvchilarga nusxalanganda, o'zgaruvchilar ob'ektning xotiradagi manzilini o'z ichiga oladi, shuning uchun ular bir xil ob'ektga kirish imkoniyatiga ega.
  • Ob'ektlar o'rtasidagi taqqoslash ularning havolalarini solishtiradi. Ikkala ob'ekt ham bir xil bo'lsa faqat ular teng hisoblanadi.
  • const ob'ektlari, ular o'zlari o'zgarmas bo'lsa ham, o'z xususiyatlarini o'zgartirishi mumkin.
  • Ob'ektlarni klonlash uchun Object.assign yordamida yangi ob'ekt yaratib, mavjud obyektning barcha xususiyatlarini nusxalash mumkin. Rekursiya yordamida chuqur nusxalash mumkin.

Bu Object.assign yoki tarqalish sintaksisida foydalanishni o'z ichiga olgan bo'lsa, har doim obyektning chuqur klonlanishini unutmang.

Ushbu sahifada

GitHubda tahrirlash