Loading...

Объекттерди примитивдерге айландыруу

Объекттерди примитивдерге айландыруу

 

Эгер сиз эки объектти кошуп obj1 + obj2, бирин экинчисинен алып салсаңыз obj1 - obj2же аларды экранда көрсөтсөңүз эмне болот alert(obj)?

JavaScript операторлордун объекттер менен иштешин ыңгайлаштырууга таптакыр жол бербейт. Ruby же C++ сыяктуу башка программалоо тилдеринен айырмаланып, биз кошумчаны (же башка операторлорду) иштетүү үчүн атайын объект ыкмасын ишке ашыра албайбыз.

Мындай операцияларда объекттер автоматтык түрдө примитивдерге айландырылат, андан кийин операциянын өзү бул примитивдер боюнча аткарылат жана чыгарууда примитивдик маани алабыз.

Бул маанилүү чектөө: натыйжа obj1 + obj2(же башка математикалык операция) башка объект болушу мүмкүн эмес!

Мисалы, биз векторлорду же матрицаларды (же жетишкендиктерди, же башка нерсени) чагылдырган объекттерди түзө албайбыз, аларды кошуп, натыйжада "жалпыланган" объектти күтө албайбыз. Мындай архитектуралык кыймылдар автоматтык түрдө «ашып кетет».

Ошентип, биз бул жерде техникалык жактан көп нерсе кыла албагандыктан, реалдуу долбоорлордо объектилер менен математика жок. Эгерде ал дагы деле болсо, анда сейрек учурларды эске албаганда, бул коддогу каталарга байланыштуу.

Бул бөлүмдө объект кантип примитивге айландырыларын жана аны кантип ыңгайлаштырса болорун карап чыгабыз.

Биздин эки максат бар:

  1. Бул бизге коддогу каталар, мындай операция кокусунан болгондо эмне болорун түшүнүүгө мүмкүндүк берет.
  2. Мындай операциялар мүмкүн жана абдан ылайыктуу болгон өзгөчө учурлар бар. Мисалы, кемитүү же даталарды ( Dateобъекттерди) салыштыруу. Алар менен кийинчерээк жолугушабыз.

Конверсия эрежелери

Типти конверсиялоо бөлүмүндө биз сандык, саптык жана логикалык примитивдик конверсиялардын эрежелерин карадык. Бирок биз объекттер үчүн орун калтырдык. Эми биз ыкмалар жана символдор жөнүндө билгенден кийин, бул боштукту толтурууга убакыт келди.

  1. Бульдикке эч кандай конверсия жок. Логикалык контекстте бардык объекттер , trueбул жөнөкөй. Алардын сандык жана саптык конверсиясы гана бар.
  2. Сандык конверсия объекттерди алып салганда же математикалык функцияларды колдонгондо болот. Мисалы, объекттерди ( Күнү жана УбакытDate бөлүмүндө талкууланат ) алып салууга болот жана натыйжада эки күндүн ортосундагы убакыт айырмасы пайда болот.date1 - date2
  3. 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 объектте төмөнкү үч ыкманы табууга жана чакырууга аракет кылат:

  1. Чакыруу obj[Symbol.toPrimitive](hint)– символдук ачкыч (системанын символу) бар ыкма Symbol.toPrimitive, эгерде мындай ыкма бар болсо,
  2. Болбосо, ишарат болсо"string"
    • obj.toString()же obj.valueOf(), кайсынысы бар болсо, чакырып көрүңүз .
  3. Болбосо, кыйытма "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жана жок болгон учурда valueOftoStringбардык примитивдүү конверсияларды иштетет.

Конверсия ар кандай примитивдүү түрдү кайтара алат

Бардык примитивдик конверсия ыкмалары жөнүндө билүү маанилүү нерсе, алар кыйытылган примитивди сөзсүз түрдө кайтара албайт.

toStringМетод так сапты кайтарып береби, же ыкма Symbol.toPrimitiveкыйытма үчүн так санды кайтарабы, эч кандай көзөмөл жок "number".

Жалгыз шарт - бул методдор объектти эмес, примитивди кайтарышы керек.

Тарыхый маалымдама

Тарыхый себептерден улам, эгерде объектти кайтарса toStringже valueOfкайтарса, анда эч кандай ката болбойт, бирок мындай маани этибарга алынбайт (ыкма такыр болгон эмес сыяктуу). Себеби, байыркы убакта JavaScript-те "ката" деген жакшы түшүнүк болгон эмес.

Бирок Symbol.toPrimitiveбуга чейин "тазараак", бул ыкма примитивди кайтарышы керек , антпесе ката болот.

Андан аркы трансформациялар

Белгилүү болгондой, көптөгөн операторлор жана функциялар типти өзгөртүүнү ишке ашырат, мисалы, көбөйтүү *операнддарды сандарга айлантат.

Эгерде объектти аргумент катары берсек, анда эсептөөлөр эки этап болот:

  1. Объект примитивге айландырылат (жогоруда айтылган эрежелерди колдонуу менен).
  2. Андан аркы эсептөөлөр үчүн зарыл болсо, бул примитив андан ары өзгөртүлөт.

Мисалы:

@A@let obj = {
  // toString обрабатывает все преобразования в случае отсутствия других методов
  toString() {
    return "2";
  }
};

alert(obj * 2); // 4, объект был преобразован к примитиву "2", затем умножение сделало его числом@A@
  1. Көбөйтүү obj * 2биринчи кезекте объектти примитивге айлантат (бул сап "2").
  2. Андан кийин "2" * 2болот 2 * 2(сап санга айландырылат).

Бирок, мисалы, экилик плюс окшош жагдайда сызыктарды бириктирет, анткени ал сызыктарды такыр четке какпайт:

 
@A@
let obj = {
  toString() {
    return "2";
  }
};
alert(obj + 2); // 22 ("2" + 2), преобразование к примитиву вернуло строку => конкатенация@A@

Бардыгы

Объектти примитивге айландыруу примитивди маани катары күткөн көптөгөн орнотулган функциялар жана операторлор тарабынан автоматтык түрдө чакырылат.

Бул үчүн 3 гана түрү (кеңештери) бар:

  • "string"alertсапты талап кылган башка операциялар үчүн)
  • "number"(математикалык операциялар үчүн)
  • "default"(кээ бир башка операторлор үчүн, адатта, объекттер аны катары ишке ашырат "number")

Спецификация ар бир оператор үчүн аны колдонууга тийиш болгон ишараттарды ачык сүрөттөйт.

Айландыруу алгоритми төмөнкүдөй:

  1. Метод биринчи деп аталат obj[Symbol.toPrimitive](hint), эгерде ал бар болсо,
  2. Эгерде кыйытма барабар болсо"string"
    • чакыруу аракети бар obj.toString()жана obj.valueOf()эмнеге жараша.
  3. Эгерде кыйытма "number"же менен барабар болсо"default"
    • чакыруу аракети бар obj.valueOf()жана obj.toString()эмнеге жараша.

Бул ыкмалардын баары примитивди кайтарышы керек (эгерде аныкталган болсо).

Практикада көп учурда сапты конверсиялоо үчүн жалпы ыкма катары гана ишке ашыруу жетиштүү obj.toString(), ал журналга жазуу же мүчүлүштүктөрдү оңдоо максатында объекттин адам окуй турган көрүнүшүн кайтарып бериши керек.