Що ж, як і обіцяв, рубрика "Бабіч спозоривсь на ютубі" продовжується, і сьогодні ми поглянемо до простого на перший погляд оператора +. Чому на перший погляд? Бо якщо копнути глибше, виявиться, що там не все так однозначно.
Коли ми використовуємо
+ з числовими типами, він поводиться рівно так, як ми й очікуємо ще зі школи — додає два значення, продукує суму. Є, щоправда, маленький нюанс з BigInt, той занадто гордий, аби товаришувати з іншими типами і займається арифметикою лише з іншими BigInt.Однак цікаві досліди починаються, коли ми спробуємо змішати у виразі числові і нечислові значення, або ж пододавати між собою не числа. Відразу на думку спаде "додавання" рядочків. Тільки, насправді, це не додавання, а операція конкатенації — злиття послідовних рядків в один.
І тоді оператор
+, не змінюючи вигляду, змінює роль — він перетворюється з оператора арифметичного додавання на оператор конкатенації.І ось ми підійшли до моменту, в якому я заплутався на етері. Річ у тому, що коли у виразі хоча б один операнд є рядком, другий операнд буде примусово приведено до рядка. У мене ж в голові звідкись сплило, що порядок операндів може вплинути на поведінку оператора. Насправді це не відповідає дійсності, і на поведінку операторів вплинуть лише типи операндів.
Хоча є один трюк, який може призвести до такого хибного висновку. Увівши в консолі наступні вирази, ми отримаємо такі результати:
{} + 1 // 1
1 + {} // 1[object Object]Виглядає цілком переконливо. Але є нюанс.
{} на першому рядочку не сприймається інтерпретатором як літерал обʼєкта. Знаходячись на початку рядка, {} сприймається як пустий блок коду. І в цьому випадку вираз перетворюється на + 1, що й дає в результаті одиницю.Якщо ж ви цей же вираз присвоїте змінній, усе встане на свої місця:
let x = {} + 1 // [object Object]1Поки ви геть не заплутались, хто кому куди плюсує, втуліть вподобайку цьому допису, не зволікайте
А що це за вираз такий
+ 1, і чому він валідний? Все просто — в такому записі оператор + робить ще одне перевтілення, і стає унарним плюсом. В такій іпостасі оператор намагається перетворити операнд на число. З перемінним успіхом, звичайно, якщо ваш рядок не є репрезентацією числа. І тут би пасувало глибше розібратися, що таке ця "репрезентація числа", але мені б хотілося, аби для вас це зробили більш глибоко обізнані в цій темі люди. Я ж просто познайомлю вас з цим дивовижним явищем. Отже, перше поверхневе припущення може звучати так: "Рядок може бути приведеним до числа, якщо він містить лише цифри, ʼ+ʼ, ʼ-ʼ і десятковий роздільник, та не містить літер". Логічно.
+"1000" // 1000
+"43.5" // 43.5
Але, насправді, ми можемо привести до числа рядки, що містять літери! Не усі, правда — лише визначені, і лише якщо вони знаходяться на певних позиціях. Бо тоді ми дивимось не просто на рядок, а на запис числа в певній нотації:
+"0x10" // Шістандцяткове 16
+"0b1010"; // Двійкове 10
+"0o16" // Вісімкове 14
+"10e2" // Науковий запис 1_000
// але
+"1n" // NaN, хоча 1n це BigInt
Глибше в ці нотації я також занурюватись не буду, залишу це задоволення вам для самостійного опанування.
Ви помітили, що при початку пояснення унарного плюса я в прикладі написав пробіл після плюса, а потім вже ні? Так от, обидва записи рівноцінні: "+ 1" і "+1".
І саме цю особливість використовують в іншому прикладі "сМіШнОгО jS", створюючи банан з допомогою барана:
("b" + "a" + + "a" + "a").toLowerCase()
> "banana"Причина криється саме в поведінці оператора +, а саме в цьому фрагменті:
+ + "a". Хоч він і записується з пробілом, але обробляється ось так: + +"a", де вже унарний плюс спробує привести рядок "a" до числа, в чому зазнає невдачі і поверне той самий NaN.Вираз тепер набуде виду
"b" + "a" + "NaN" + "a" і спокійнісінько конкатенується в baNaNa, який потім майстерно приховають з .toLowerCase.Ну і хто тепер ба
Товариство, а ви як, дивуєтесь плюсу, чи знаєте усі його нюанси назубок? ;)
@babichdev