Объекттер менен примитивдердин ортосундагы принципиалдуу айырмачылыктардын бири - объекттер "маалымат боюнча" сакталат жана көчүрүлөт, ал эми примитивдүү баалуулуктар: саптар, сандар, логикалык маанилер ж. ар дайым "бүтүн сан катары" көчүрүлөт.
Эгер биз маанини көчүргөндө эмне болорун бир аз карап көрсөк, муну түшүнүү оңой.
Сап сыяктуу примитивден баштайлы.
Бул жерде биз көчүрмөсүн message
киргизебиз phrase
:
@A@let message = "Привет!";
let phrase = message;@A@
Натыйжада, бизде эки көз карандысыз өзгөрмө бар, алардын ар бири сапты сактайт "Привет!"
.
Абдан айкын натыйжа, туурабы?
Объекттер ар кандай жүрүшөт.
Объектке ыйгарылган өзгөрмө объекттин өзүн сактабайт, бирок анын "эстутум дареги", башкача айтканда, ага "маалымат".
Мындай өзгөрмөнүн мисалын карап көрөлү:
@A@let user = {
name: "John"
};@A@
Ал эми эстутумда бул жерде сакталат:
Объект эстутумдун бир жеринде (сүрөттүн оң жагында) сакталат, ал эми өзгөрмөдө user
(солдо) ага гана "маалымат" бар.
user
Биз объекттин дареги жазылган кагаз сыяктуу объекттин өзгөрмөсүн ойлонсок болот .
Объектке кыймыл-аракетти жасаганда, мисалы, касиетти алуу user.name
, JavaScript кыймылдаткычы ошол даректе эмне бар экенин карап, объекттин өзүндө операцияны аткарат.
Эми бул эмне үчүн маанилүү.
Объекттин өзгөрмөсүн көчүрүүдө шилтеме көчүрүлөт, бирок объекттин өзү кайталанбайт.
Мисалы:
@A@let user = { name: "John" };
let admin = user; // копируется ссылка@A@
Азыр бизде эки өзгөрмө бар, алардын ар бири бир эле объектке шилтемени камтыйт:
Көрүнүп тургандай, дагы эле бир объект бар, бирок азыр ага тиешелүү эки өзгөрмө бар.
Биз объектке жетүү жана анын мазмунун өзгөртүү үчүн каалаган өзгөрмөлөрдү колдоно алабыз:
@A@let user = { name: 'John' };
let admin = user;
admin.name = 'Pete'; // изменено по ссылке из переменной "admin"
alert(user.name); // 'Pete', изменения видны по ссылке из переменной "user"@A@
Бул эки ачкычтуу шкафка ээ болуу жана алардын бирин ( admin
) колдонуу менен ага кирип, өзгөртүүлөрдү киргизүү сыяктуу. Анан кийинчерээк башка ачкычты ( ) колдонсок user
, биз дагы эле ошол эле шкафты ачып, өзгөртүлгөн мазмунга кире алабыз.
Шилтеме боюнча салыштыруу
Эки объект бирдей объект болгондо гана бирдей болот.
Мисалы, бул жерде a
жана b
бир эле объектке кайрылыңыз, ошондуктан алар бирдей:
@A@let a = {};
let b = a; // копирование по ссылке
alert( a == b ); // true, обе переменные ссылаются на один и тот же объект
alert( a === b ); // true@A@
Ал эми бул жерде эки көз карандысыз объект бирдей көрүнсө да, бирдей эмес (экөө тең бош):
@A@let a = {};
let b = {}; // два независимых объекта
alert( a == b ); // false@A@
Типти салыштыруу obj1 > obj2
же примитивдүү салыштыруу үчүн obj == 5
объекттер примитивдерге айландырылат. Объектти конвертациялоо кантип иштээрин биз жакында билебиз, бирок чындык мындай салыштыруулар сейрек талап кылынат жана адатта программист каталарынан келип чыгат.
Клондоо жана бириктирүү, Object.assign
Ошентип, объекттин өзгөрмөсүн көчүрүү бир эле объектке дагы бир шилтемени түзөт.
Бирок биз дагы эле объектти кайталап керек болсочу? көз карандысыз көчүрмөсүн түзүү, клон?
Бул дагы жасалса болот, бирок бир аз татаалыраак, анткени JavaScript бул үчүн орнотулган ыкмага ээ эмес. Бирок, чындыгында, бул сейрек зарыл, көпчүлүк учурларда шилтеме боюнча көчүрүү жетиштүү.
Бирок, эгерде биз муну чындап кааласак, анда биз жаңы объект түзүп, анын касиеттерин кайталоо жана аларды примитивдүү деңгээлде көчүрүү менен учурдагы объектинин структурасын кайра чыгарышыбыз керек.
Мисалы, бул сыяктуу:
@A@let user = {
name: "John",
age: 30
};
let clone = {}; // новый пустой объект
// давайте скопируем все свойства user в него
for (let key in user) {
clone[key] = user[key];
}
// теперь clone это полностью независимый объект с тем же содержимым
clone.name = "Pete"; // изменим в нём данные
alert( user.name ); // все ещё John в первоначальном объекте@A@
Бул үчүн Object.assign ыкмасын да колдонсок болот .
Синтаксис:
Object.assign(dest, [src1, src2, src3...])
- Биринчи аргумент
dest
максаттуу объект болуп саналат. - Калган аргументтер
src1, ..., srcN
(керек болушу мүмкүн) баштапкы объекттер - Метод бардык баштапкы объекттердин касиеттерин
src1, ..., srcN
максаттуу объектке көчүрөтdest
. Башкача айтканда, экинчиден баштап бардык аргументтердин касиеттери биринчи объектке көчүрүлөт. - Объектти кайтарат
dest
.
Мисалы, биз аны бир нече объектилерди бир объектке бириктирүү үчүн колдоно алабыз:
let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
// копируем все свойства из permissions1 и permissions2 в user
Object.assign(user, permissions1, permissions2);
// теперь user = { name: "John", canView: true, canEdit: true }
Эгерде көчүрүлгөн менчиктин аты мурунтан эле бар болсо, анын үстүнө жазылат:
let user = { name: "John" };
Object.assign(user, { name: "Pete" });
alert(user.name); // теперь user = { name: "Pete" }
Object.assign
Жөнөкөй клондоо үчүн циклди алмаштыруу үчүн да колдоно алабыз for..in
:
let user = {
name: "John",
age: 30
};
let clone = Object.assign({}, user);
Ал бардык касиеттерди user
бош объектке көчүрүп, аны кайтарат.
Объектти клондоштуруунун башка ыкмалары да бар. Мисалы, кийинчерээк окуу куралында талкууланган жайылтуу операторун колдонуу. clone = {...user}
Nested Cloning
user
Буга чейин биз бардык касиеттер примитивдүү деп ойлогонбуз . Бирок касиеттер башка объекттерге шилтеме болушу мүмкүн. Алар менен эмне кылуу керек?
Мисалы, бир объект бар:
@A@let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
alert( user.sizes.height ); // 182@A@
Эми жөн эле көчүрүү жетишсиз clone.sizes = user.sizes
, анткени user.sizes
объект болуп саналат, ал шилтеме аркылуу көчүрүлөт. Ошентип, clone
жана user
жалпы объект болот sizes
:
@A@let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
let clone = Object.assign({}, user);
alert( user.sizes === clone.sizes ); // true, тот же объект
// user и clone обладают общим свойством sizes
user.sizes.width++; // изменяем свойства в первом объекте
alert(clone.sizes.width); // 51, видим результат в другом@A@
Муну оңдоо үчүн биз клон циклин колдонушубуз керек, ал ар бир маанини текшерет user[key]
жана ал объект болсо, анда анын структурасын да көчүрөт. Бул "терең клондоо" деп аталат.
Биз рекурсияны колдонуу менен терең клондоштурууну ишке ашыра алабыз. Же дөңгөлөктү кайра ойлоп таппоо үчүн, lodash JavaScript китепканасынан _.cloneDeep(obj) сыяктуу даяр ишке ашырууну алыңыз .
Биз ошондой эле глобалдык структуралык Clone() ыкмасын колдоно алабыз , ал бизге объекттин толук көчүрмөсүн жасоого мүмкүндүк берет. Тилекке каршы, аны заманбап браузерлер гана колдойт. Бул ыкма үчүн колдоо таба аласыз бул жерден .
Объекттерди шилтемелер катары сактоонун маанилүү терс таасири болуп жарыяланган объект өзгөртүлүшүconst
мүмкүн .
Мисалы:
@A@const user = {
name: "John"
};
user.name = "Pete"; // (*)
alert(user.name); // Pete@A@
(*)
Бул сызык катага алып келет окшойт , бирок андай эмес. Мааниси user
туруктуу, ал ар дайым бир эле объектке кайрылышы керек, бирок ал объекттин касиеттери өзгөрө алат.
Башка сөз менен айтканда, эгерде биз жалпылап const user
коюуга аракет кылсак, ал ката берет .user=...
Бирок, биз чындап эле туруктуу объект касиеттерин түзүү керек болсо, бул да мүмкүн, бирок такыр башка ыкмаларды колдонуу менен. Биз бул тууралуу Желектердин жана мүлктүн дескрипторлору бөлүмүндө токтолобуз .
Бардыгы
Объекттер шилтеме аркылуу дайындалат жана көчүрүлөт. Башкача айтканда, өзгөрмө "объекттин маанисин" сактабайт, бирок ошол мааниге "маалымат" (эстутум дареги) сактайт. Ошентип, мындай өзгөрмөнү көчүрүү же аны функциянын аргументи катары берүү объекттин өзүн эмес, ошол шилтемени көчүрөт.
Көчүрүлгөн шилтемелерди колдонуу менен бардык операциялар (мисалы, касиеттерди кошуу/алып салуу) бир эле объектте аткарылат.
"Чыныгы көчүрмөнү" (клон) түзүү үчүн биз "тайыз көчүрмө" деп аталган нерсени (ичке салынган объекттер шилтеме аркылуу көчүрүлөт) же _.cloneDeep(obj)Object.assign
сыяктуу "терең клон" функциясын колдоно алабыз .
Объекттерди жана шилтемелерди көчүрүү
Объекттер менен примитивдердин ортосундагы принципиалдуу айырмачылыктардын бири - объекттер "маалымат боюнча" сакталат жана көчүрүлөт, ал эми примитивдүү баалуулуктар: саптар, сандар, логикалык маанилер ж. ар дайым "бүтүн сан катары" көчүрүлөт.
Эгер биз маанини көчүргөндө эмне болорун бир аз карап көрсөк, муну түшүнүү оңой.
Сап сыяктуу примитивден баштайлы.
Бул жерде биз көчүрмөсүн message
киргизебиз phrase
:
@A@let message = "Привет!";
let phrase = message;@A@
Натыйжада, бизде эки көз карандысыз өзгөрмө бар, алардын ар бири сапты сактайт "Привет!"
Абдан айкын натыйжа, туурабы?
Объекттер ар кандай жүрүшөт.
Объектке ыйгарылган өзгөрмө объекттин өзүн сактабайт, бирок анын "эстутум дареги", башкача айтканда, ага "маалымат".
Мындай өзгөрмөнүн мисалын карап көрөлү:
@A@let user = {
name: "John"
};@A@
Ал эми эстутумда бул жерде сакталат
Объект эстутумдун бир жеринде (сүрөттүн оң жагында) сакталат, ал эми өзгөрмөдө user
(солдо) ага гана "маалымат" бар.
user
Биз объекттин дареги жазылган кагаз сыяктуу объекттин өзгөрмөсүн ойлонсок болот .
Объектке кыймыл-аракетти жасаганда, мисалы, касиетти алуу user.name
, JavaScript кыймылдаткычы ошол даректе эмне бар экенин карап, объекттин өзүндө операцияны аткарат.
Эми бул эмне үчүн маанилүү.
Объекттин өзгөрмөсүн көчүрүүдө шилтеме көчүрүлөт, бирок объекттин өзү кайталанбайт.
Мисалы:
@A@let user = { name: "John" };
let admin = user; // копируется ссылка@A@
Азыр бизде эки өзгөрмө бар, алардын ар бири бир эле объектке шилтемени камтыйт
Көрүнүп тургандай, дагы эле бир объект бар, бирок азыр ага тиешелүү эки өзгөрмө бар.
Биз объектке жетүү жана анын мазмунун өзгөртүү үчүн каалаган өзгөрмөлөрдү колдоно алабыз:
@A@let user = { name: 'John' };
let admin = user;
admin.name = 'Pete'; // изменено по ссылке из переменной "admin"
alert(user.name); // 'Pete', изменения видны по ссылке из переменной "user"@A@
Бул эки ачкычтуу шкафка ээ болуу жана алардын бирин ( admin
) колдонуу менен ага кирип, өзгөртүүлөрдү киргизүү сыяктуу. Анан кийинчерээк башка ачкычты ( ) колдонсок user
, биз дагы эле ошол эле шкафты ачып, өзгөртүлгөн мазмунга кире алабыз.
Шилтеме боюнча салыштыруу
Эки объект бирдей объект болгондо гана бирдей болот.
Мисалы, бул жерде a
жана b
бир эле объектке кайрылыңыз, ошондуктан алар бирдей:
@AW@let a = {};
let b = a; // копирование по ссылке
alert( a == b ); // true, обе переменные ссылаются на один и тот же объект
alert( a === b ); // true@A@
Ал эми бул жерде эки көз карандысыз объект бирдей көрүнсө да, бирдей эмес (экөө тең бош):
@A@let a = {};
let b = {}; // два независимых объекта
alert( a == b ); // false@A@
Типти салыштыруу obj1 > obj2
же примитивдүү салыштыруу үчүн obj == 5
объекттер примитивдерге айландырылат. Объектти конвертациялоо кантип иштээрин биз жакында билебиз, бирок чындык мындай салыштыруулар сейрек талап кылынат жана адатта программист каталарынан келип чыгат.
Клондоо жана бириктирүү, Object.assign
Ошентип, объекттин өзгөрмөсүн көчүрүү бир эле объектке дагы бир шилтемени түзөт.
Бирок биз дагы эле объектти кайталап керек болсочу? көз карандысыз көчүрмөсүн түзүү, клон?
Бул дагы жасалса болот, бирок бир аз татаалыраак, анткени JavaScript бул үчүн орнотулган ыкмага ээ эмес. Бирок, чындыгында, бул сейрек зарыл, көпчүлүк учурларда шилтеме боюнча көчүрүү жетиштүү.
Бирок, эгерде биз муну чындап кааласак, анда биз жаңы объект түзүп, анын касиеттерин кайталоо жана аларды примитивдүү деңгээлде көчүрүү менен учурдагы объектинин структурасын кайра чыгарышыбыз керек.
Мисалы, бул сыяктуу:
@A@let user = {
name: "John",
age: 30
};
let clone = {}; // новый пустой объект
// давайте скопируем все свойства user в него
for (let key in user) {
clone[key] = user[key];
}
// теперь clone это полностью независимый объект с тем же содержимым
clone.name = "Pete"; // изменим в нём данные
alert( user.name ); // все ещё John в первоначальном объекте
Бул үчүн Object.assign ыкмасын да колдонсок болот .
Синтаксис:
Object.assign(dest, [src1, src2, src3...])@A@
- Биринчи аргумент
dest
максаттуу объект болуп саналат. - Калган аргументтер
src1, ..., srcN
(керек болушу мүмкүн) баштапкы объекттер - Метод бардык баштапкы объекттердин касиеттерин
src1, ..., srcN
максаттуу объектке көчүрөтdest
. Башкача айтканда, экинчиден баштап бардык аргументтердин касиеттери биринчи объектке көчүрүлөт. - Объектти кайтарат
dest
.
Мисалы, биз аны бир нече объектилерди бир объектке бириктирүү үчүн колдоно алабыз:
@A@let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
// копируем все свойства из permissions1 и permissions2 в user
Object.assign(user, permissions1, permissions2);
// теперь user = { name: "John", canView: true, canEdit: true }@A@
Эгерде көчүрүлгөн менчиктин аты мурунтан эле бар болсо, анын үстүнө жазылат:
@A@let user = { name: "John" };
Object.assign(user, { name: "Pete" });
alert(user.name); // теперь user = { name: "Pete" }@A@
Object.assign
Жөнөкөй клондоо үчүн циклди алмаштыруу үчүн да колдоно алабыз for..in
:
@A@let user = {
name: "John",
age: 30
};
let clone = Object.assign({}, user);@A@
Ал бардык касиеттерди user
бош объектке көчүрүп, аны кайтарат.
Объектти клондоштуруунун башка ыкмалары да бар. Мисалы, кийинчерээк окуу куралында талкууланган жайылтуу операторун колдонуу. clone = {...user}
Nested Cloning
user
Буга чейин биз бардык касиеттер примитивдүү деп ойлогонбуз . Бирок касиеттер башка объекттерге шилтеме болушу мүмкүн. Алар менен эмне кылуу керек?
Мисалы, бир объект бар:
@A@let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
alert( user.sizes.height ); // 182@A@
Эми жөн эле көчүрүү жетишсиз clone.sizes = user.sizes
, анткени user.sizes
объект болуп саналат, ал шилтеме аркылуу көчүрүлөт. Ошентип, clone
жана user
жалпы объект болот sizes
:
@A@let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
let clone = Object.assign({}, user);
alert( user.sizes === clone.sizes ); // true, тот же объект
// user и clone обладают общим свойством sizes
user.sizes.width++; // изменяем свойства в первом объекте
alert(clone.sizes.width); // 51, видим результат в другом@A@
Муну оңдоо үчүн биз клон циклин колдонушубуз керек, ал ар бир маанини текшерет user[key]
жана ал объект болсо, анда анын структурасын да көчүрөт. Бул "терең клондоо" деп аталат.
Биз рекурсияны колдонуу менен терең клондоштурууну ишке ашыра алабыз. Же дөңгөлөктү кайра ойлоп таппоо үчүн, lodash JavaScript китепканасынан _.cloneDeep(obj) сыяктуу даяр ишке ашырууну алыңыз .
Биз ошондой эле глобалдык структуралык Clone() ыкмасын колдоно алабыз , ал бизге объекттин толук көчүрмөсүн жасоого мүмкүндүк берет. Тилекке каршы, аны заманбап браузерлер гана колдойт. Бул ыкма үчүн колдоо таба аласыз бул жерден .
Объекттерди шилтемелер катары сактоонун маанилүү терс таасири болуп жарыяланган объект өзгөртүлүшүconst
мүмкүн .
Мисалы:
@A@const user = {
name: "John"
};
user.name = "Pete"; // (*)
alert(user.name); // Pete@A@
(*)
Бул сызык катага алып келет окшойт , бирок андай эмес. Мааниси user
туруктуу, ал ар дайым бир эле объектке кайрылышы керек, бирок ал объекттин касиеттери өзгөрө алат.
Башка сөз менен айтканда, эгерде биз жалпылап const user
коюуга аракет кылсак, ал ката берет .user=...
Бирок, биз чындап эле туруктуу объект касиеттерин түзүү керек болсо, бул да мүмкүн, бирок такыр башка ыкмаларды колдонуу менен. Биз бул тууралуу Желектердин жана мүлктүн дескрипторлору бөлүмүндө токтолобуз .
Бардыгы
Объекттер шилтеме аркылуу дайындалат жана көчүрүлөт. Башкача айтканда, өзгөрмө "объекттин маанисин" сактабайт, бирок ошол мааниге "маалымат" (эстутум дареги) сактайт. Ошентип, мындай өзгөрмөнү көчүрүү же аны функциянын аргументи катары берүү объекттин өзүн эмес, ошол шилтемени көчүрөт.
Көчүрүлгөн шилтемелерди колдонуу менен бардык операциялар (мисалы, касиеттерди кошуу/алып салуу) бир эле объектте аткарылат.
"Чыныгы көчүрмөнү" (клон) түзүү үчүн биз "тайыз көчүрмө" деп аталган нерсени (ичке салынган объекттер шилтеме аркылуу көчүрүлөт) же _.cloneDeep(obj)Object.assign
сыяктуу "терең клон" функциясын колдоно алабыз .