Эгер сиз эки объектти кошуп obj1 + obj2
, бирин экинчисинен алып салсаңыз obj1 - obj2
же аларды экранда көрсөтсөңүз эмне болот alert(obj)
?
JavaScript операторлордун объекттер менен иштешин ыңгайлаштырууга таптакыр жол бербейт. Ruby же C++ сыяктуу башка программалоо тилдеринен айырмаланып, биз кошумчаны (же башка операторлорду) иштетүү үчүн атайын объект ыкмасын ишке ашыра албайбыз.
Мындай операцияларда объекттер автоматтык түрдө примитивдерге айландырылат, андан кийин операциянын өзү бул примитивдер боюнча аткарылат жана чыгарууда примитивдик маани алабыз.
Бул маанилүү чектөө: натыйжа obj1 + obj2
(же башка математикалык операция) башка объект болушу мүмкүн эмес!
Мисалы, биз векторлорду же матрицаларды (же жетишкендиктерди, же башка нерсени) чагылдырган объекттерди түзө албайбыз, аларды кошуп, натыйжада "жалпыланган" объектти күтө албайбыз. Мындай архитектуралык кыймылдар автоматтык түрдө «ашып кетет».
Ошентип, биз бул жерде техникалык жактан көп нерсе кыла албагандыктан, реалдуу долбоорлордо объектилер менен математика жок. Эгерде ал дагы деле болсо, анда сейрек учурларды эске албаганда, бул коддогу каталарга байланыштуу.
Бул бөлүмдө объект кантип примитивге айландырыларын жана аны кантип ыңгайлаштырса болорун карап чыгабыз.
Биздин эки максат бар:
- Бул бизге коддогу каталар, мындай операция кокусунан болгондо эмне болорун түшүнүүгө мүмкүндүк берет.
- Мындай операциялар мүмкүн жана абдан ылайыктуу болгон өзгөчө учурлар бар. Мисалы, кемитүү же даталарды (
Date
объекттерди) салыштыруу. Алар менен кийинчерээк жолугушабыз.
Конверсия эрежелери
Типти конверсиялоо бөлүмүндө биз сандык, саптык жана логикалык примитивдик конверсиялардын эрежелерин карадык. Бирок биз объекттер үчүн орун калтырдык. Эми биз ыкмалар жана символдор жөнүндө билгенден кийин, бул боштукту толтурууга убакыт келди.
- Бульдикке эч кандай конверсия жок. Логикалык контекстте бардык объекттер ,
true
бул жөнөкөй. Алардын сандык жана саптык конверсиясы гана бар. - Сандык конверсия объекттерди алып салганда же математикалык функцияларды колдонгондо болот. Мисалы, объекттерди ( Күнү жана Убакыт
Date
бөлүмүндө талкууланат ) алып салууга болот жана натыйжада эки күндүн ортосундагы убакыт айырмасы пайда болот.date1 - date2
alert(obj)
Сапты конвертациялоого келсек, ал көбүнчө объектти окшош контексттерде көрсөткөндө болот .
Биз атайын объекттик ыкмаларды колдонуу менен саптарга жана сандарга өзүбүздүн конвертацияларды ишке ашыра алабыз.
Эми майда-чүйдөсүнө чейин кирип көрөлү. Бул теманын нюанстарын түшүнүүнүн жалгыз жолу.
кыйытмалар
Кайсы трансформацияны колдонууну JavaScript кантип чечет?
Түрдүү конверсиянын үч түрү бар, алар ар кандай кырдаалдарда пайда болот. спецификацияда айтылгандай, алар "кеңештер" деп аталат :
"string"
Объектти сапка айландыруу үчүн, сапты күткөн объектке операция жасаганда, мисалы alert
:
@A@// вывод
alert(obj);
// используем объект в качестве ключа
anotherObj[obj] = 123;
"number"@A@
Математикалык операцияларда объектти санга айландыруу үчүн:
@A@// явное преобразование
let num = Number(obj);
// математические (не считая бинарного плюса)
let n = +obj; // унарный плюс
let delta = date1 - date2;
// сравнения больше/меньше
let greater = user1 > user2;@A@
Орнотулган математикалык функциялардын көбү бул конверсияны камтыйт.
"default"
Оператор кайсы түрдү күтөөрүн "билбеген" учурда сейрек кездешет.
Мисалы, бинардык плюс +
эки сап менен (аларды бириктирип) жана сандар менен (аларды кошуу) иштей алат. Демек, экилик плюс объектти аргумент катары алса, "default"
аны айландыруу үчүн кыйытма колдонот.
Ошондой эле, эгер объект ==
сап, сан же символ менен салыштырылса, кандай конвертация жасалышы керектиги да түшүнүксүз, андыктан кыйытма колдонулат "default"
.
@A@// бинарный плюс использует хинт "default"
let total = obj1 + obj2;
// obj == number использует хинт "default"
if (user == 1) { ... };@A@
Салыштыруудан чоңураак/кичинекей операторлор, мисалы <
>
, саптарда да, сандарда да иштей алат. "number"
Бирок, тарыхый себептерден улам алар эмес , кыйытты колдонушат "default"
.
Бирок, иш жүзүндө, баары бир аз жеңил болот.
Биринен башка бардык орнотулган объекттер ( Date
кийин карап чыгабыз) "default"
конверсияны , сыяктуу эле ишке ашырат "number"
. Жана биз да ошондой кылышыбыз керек.
Конверсияны ишке ашыруу үчүн JavaScript объектте төмөнкү үч ыкманы табууга жана чакырууга аракет кылат:
- Чакыруу
obj[Symbol.toPrimitive](hint)
– символдук ачкыч (системанын символу) бар ыкмаSymbol.toPrimitive
, эгерде мындай ыкма бар болсо, - Болбосо, ишарат болсо
"string"
obj.toString()
жеobj.valueOf()
, кайсынысы бар болсо, чакырып көрүңүз .
- Болбосо, кыйытма
"number"
же менен барабар болсо"default"
obj.valueOf()
жеobj.toString()
, кайсынысы бар болсо, чакырып көрүңүз .
Symbol.toPrimitive
Биринчи ыкма менен баштайлы. аттуу орнотулган символ бар Symbol.toPrimitive
, ал конверсия ыкмасын белгилөө үчүн колдонулушу керек, мисалы:
@A@obj[Symbol.toPrimitive] = function(hint) {
// вот код для преобразования этого объекта в примитив
// он должен вернуть примитивное значение
// hint = чему-то из "string", "number", "default"
};@A@
Эгерде метод Symbol.toPrimitive
бар болсо, ал бардык кеңештер үчүн колдонулат жана башка ыкмалар талап кылынбайт.
Мисалы, бул жерде user
аны ишке ашыруучу объект бар:
@A@let user = {
name: "John",
money: 1000,
[Symbol.toPrimitive](hint) {
alert(`hint: ${hint}`);
return hint == "string" ? `{name: "${this.name}"}` : this.money;
}
};
// демонстрация результатов преобразований:
alert(user); // hint: string -> {name: "John"}
alert(+user); // hint: number -> 1000
alert(user + 500); // hint: default -> 1500@A@
Коддон көрүнүп тургандай, user
ал сыпаттамасы бар сапка, же трансформацияга жараша акчага айланат. Бир ыкма user[Symbol.toPrimitive]
бардык конверсия учурларын чечет.
toString/valueOf
Эгерде андай болбосо Symbol.toPrimitive
, анда JavaScript ыкмаларды табууга аракет кылат toString
жана valueOf
:
- Кеңеш үчүн
"string"
: методду чакырыңызtoString
жана ал жок болсо же примитивдик маанинин ордуна объектти кайтарса, андаvalueOf
(ошондуктанtoString
сапты конверсиялоодо артыкчылык берилет). - Башка кеңештер үчүн: методду чакырыңыз
valueOf
жана ал жок болсо же примитивдик маанинин ордуна объектти кайтарса, андаtoString
(ошондуктанvalueOf
математикалык операциялар үчүн артыкчылык берилет).
Методдор toString
жана valueOf
байыркы доорлордон келип чыккан. Бул символдор эмес (ал кезде символдор болгон эмес), тескерисинче, сап аталыштары менен жөн гана "кадимки" ыкмалар. Алар трансформацияны ишке ашыруунун альтернативалуу “эски” жолун сунуштайт.
Бул ыкмалар примитивдүү маанини кайтарышы керек. Эгерде toString
же valueOf
объектти кайтарса, анда ал этибарга алынбайт (эч кандай ыкма жок сыяктуу).
Демейки боюнча, кадимки объектте төмөнкү ыкмалар toString
жана valueOf
:
- Метод
toString
сапты кайтарат"[object Object]"
. - Метод
valueOf
объекттин өзүн кайтарат.
Мисалга карап көрүңүз:
@A@let user = {name: "John"};
alert(user); // [object Object]
alert(user.valueOf() === user); // true@A@
Ошентип, эгер биз объектти сап катары колдонууга аракет кылсак, ушуга окшош alert
же ушул сыяктуу, анда демейки боюнча биз көрөбүз [object Object]
.
Демейки маани valueOf
бул жерде кандайдыр бир башаламандыкты болтурбоо үчүн толуктук үчүн гана айтылган. Көрүнүп тургандай, ал объекттин өзүн кайтарат жана ошондуктан көңүл бурулбайт. Эмнеге деп сурабагыла, бул тарыхый себептерден улам. Ошентип, биз ал жок деп болжолдоого болот.
Келгиле, трансформацияны ыңгайлаштыруу үчүн бул ыкмаларды колдонолу.
Мисалы, биз аларды бир эле объектти ишке ашырууда колдонобуз user
. Бирок буга чейин айкалыштыруу toString
жана valueOf
анын ордуна Symbol.toPrimitive
@A@let user = {
name: "John",
money: 1000,
// для хинта равного "string"
toString() {
return `{name: "${this.name}"}`;
},
// для хинта равного "number" или "default"
valueOf() {
return this.money;
}
};
alert(user); // toString -> {name: "John"}
alert(+user); // valueOf -> 1000
alert(user + 500); // valueOf -> 1500@A@
Көрүнүп тургандай, биз мурунку мисалдагыдай эле жүрүм-турумга ээ болдук Symbol.toPrimitive
.
Көп учурда бардык примитивдүү трансформацияларды жүргүзүү үчүн бизге бирдиктүү "универсалдуу" жер керектелет. Бул учурда, биз бир гана ишке ашыра алабыз toString
:
@A@let user = {
name: "John",
toString() {
return this.name;
}
};
alert(user); // toString -> John
alert(user + 500); // toString -> John500@A@
Symbol.toPrimitive
жана жок болгон учурда valueOf
, toString
бардык примитивдүү конверсияларды иштетет.
Конверсия ар кандай примитивдүү түрдү кайтара алат
Бардык примитивдик конверсия ыкмалары жөнүндө билүү маанилүү нерсе, алар кыйытылган примитивди сөзсүз түрдө кайтара албайт.
toString
Метод так сапты кайтарып береби, же ыкма Symbol.toPrimitive
кыйытма үчүн так санды кайтарабы, эч кандай көзөмөл жок "number"
.
Жалгыз шарт - бул методдор объектти эмес, примитивди кайтарышы керек.
Тарыхый себептерден улам, эгерде объектти кайтарса toString
же valueOf
кайтарса, анда эч кандай ката болбойт, бирок мындай маани этибарга алынбайт (ыкма такыр болгон эмес сыяктуу). Себеби, байыркы убакта JavaScript-те "ката" деген жакшы түшүнүк болгон эмес.
Бирок Symbol.toPrimitive
буга чейин "тазараак", бул ыкма примитивди кайтарышы керек , антпесе ката болот.
Андан аркы трансформациялар
Белгилүү болгондой, көптөгөн операторлор жана функциялар типти өзгөртүүнү ишке ашырат, мисалы, көбөйтүү *
операнддарды сандарга айлантат.
Эгерде объектти аргумент катары берсек, анда эсептөөлөр эки этап болот:
- Объект примитивге айландырылат (жогоруда айтылган эрежелерди колдонуу менен).
- Андан аркы эсептөөлөр үчүн зарыл болсо, бул примитив андан ары өзгөртүлөт.
Мисалы:
@A@let obj = {
// toString обрабатывает все преобразования в случае отсутствия других методов
toString() {
return "2";
}
};
alert(obj * 2); // 4, объект был преобразован к примитиву "2", затем умножение сделало его числом@A@
- Көбөйтүү
obj * 2
биринчи кезекте объектти примитивге айлантат (бул сап"2"
). - Андан кийин
"2" * 2
болот2 * 2
(сап санга айландырылат).
Бирок, мисалы, экилик плюс окшош жагдайда сызыктарды бириктирет, анткени ал сызыктарды такыр четке какпайт:
let obj = {
toString() {
return "2";
}
};
alert(obj + 2); // 22 ("2" + 2), преобразование к примитиву вернуло строку => конкатенация@A@
Бардыгы
Объектти примитивге айландыруу примитивди маани катары күткөн көптөгөн орнотулган функциялар жана операторлор тарабынан автоматтык түрдө чакырылат.
Бул үчүн 3 гана түрү (кеңештери) бар:
"string"
(alert
сапты талап кылган башка операциялар үчүн)"number"
(математикалык операциялар үчүн)"default"
(кээ бир башка операторлор үчүн, адатта, объекттер аны катары ишке ашырат"number"
)
Спецификация ар бир оператор үчүн аны колдонууга тийиш болгон ишараттарды ачык сүрөттөйт.
Айландыруу алгоритми төмөнкүдөй:
- Метод биринчи деп аталат
obj[Symbol.toPrimitive](hint)
, эгерде ал бар болсо, - Эгерде кыйытма барабар болсо
"string"
- чакыруу аракети бар
obj.toString()
жанаobj.valueOf()
эмнеге жараша.
- чакыруу аракети бар
- Эгерде кыйытма
"number"
же менен барабар болсо"default"
- чакыруу аракети бар
obj.valueOf()
жанаobj.toString()
эмнеге жараша.
- чакыруу аракети бар
Бул ыкмалардын баары примитивди кайтарышы керек (эгерде аныкталган болсо).
Практикада көп учурда сапты конверсиялоо үчүн жалпы ыкма катары гана ишке ашыруу жетиштүү obj.toString()
, ал журналга жазуу же мүчүлүштүктөрдү оңдоо максатында объекттин адам окуй турган көрүнүшүн кайтарып бериши керек.