Skip to content

Latest commit

 

History

History
38 lines (24 loc) · 7.19 KB

Exploring JavaScripts Quirky Behaviors - {} + [] and [] + {}.hy.md

File metadata and controls

38 lines (24 loc) · 7.19 KB

JavaScript-ի զվարճալի տարօրինակությունները շարքից՝ դատարկ օբյեկտին գումարած դատարկ զանգվածը հավասար է 0, բայց դատարկ զանգվածին գումարած դատարկ օբյեկտը՝ "[object Object]": Այնուամենայնիվ՝ {} + [] === [] + {} արտահայտությունը վերադարձնում է true: Ինչու՞ տեղափոխական հատկությունը (Commutative property) մի դեպքում չի գործում, իսկ մյուս դեպքում անխափան աշխատում է:

Գումարման կանոնները JavaScript-ում շատ պարզ են։ Մենք կարող ենք իրար գումարել միայն թվեր և համակցել տողեր (Number, BigInt և String տվյալների տիպեր): Բոլոր մնացած տիպերին պատկանող արժեքների գումարման դեպքում այդ արժեքներն անուղղակի փոխակերպվում են կամ Number, կամ String տիպի, եթե այդ փոխակերպումը հնարավոր չէ, ապա ստանում ենք տիպերի վերափոխման հետ կապված սխալ՝ TypeError։ Այս բոլոր կանոնները մանրամասն նկարագրված են լեզվի ստանդարտում՝ ECMA-262-ում։ Այժմ տեսնենք թե ինչ տրամաբանությամբ են կատարվում այդ անուղղակի փոխակերպումները՝ դատարկ օբյեկտի և դատարկ զանգվածի գումարման ժամանակ, և ինչու՞ մի դեպքում գումարման տեղափոխական հատկությունը չի գործում, իսկ մյուս դեպքում այն նորմալ աշխատում է։

Նախ {} + [] արտահայտության մասին։ Բրաուզերներում ներդրված ինտերպրետատորն արտահայտության ձախ մասում գտնվող դատարկ օբյեկտն ընդունում է որպես կոդի բլոկ։ Եվ քանի-որ ձևավոր փակագծերի մեջ գտնվող բլոկը դատարկ է, ինտերպրետատորն ուղղակի անտեսում է այն, և անցնելով առաջ՝ զանգվածի առջևում դրված + օպերատորն արդեն ընկալում է որպես ունար պլյուս։ Ստացվում է, որ {} + [] արտահայտությունն ինտերպրետատորը տեսնում է որպես ինչ-որ դատարկ կոդի բլոկ, որը պետք է անտեսվի, և դատարկ զանգված, որի առջև դրված է ունար պլյուս։ Ինչպես գիտենք ունար պլյուսի օպերատորը ցանկացած տիպի պատկանող արժեք փորձում է վերածել թվային տիպի, այն կարելի է ասել ցանկացած տիպի վրա կանչում է Number() ֆունկցիան, և շատ հաճախ հենց այդ նպատակով էլ օգտագործվում է, որպես Number() ֆունկցիայի կանչի կարճ գրելաձև։

+[] === 0; // true

+[] արտահայտությունն ամբողջովին համարժեք է Number([]) արտահայտությանը և ընդամենը հանդիսանում է համառոտ գրելաձև՝ օգնելով կրճատել գրվող կոդի ծավալը։

Հաջորդ արտահայտությունը՝ [] + {}, ինտերպրետատորի կողմից այլ կերպ է դիտարկվում։ Պլյուս օպերատորն այստեղ ընկալվում է որպես համակցման օպերատոր, հետևաբար դատարկ զանգվածը վերածվում է դատարկ տողի, քանի-որ ինչպես գիտենք լեզվի՝ տիպերի անուղղակի վերափոխման կանոններով այդպես է նախատեսված։ Տակից դատարկ զանգվածի վրա կանչվում է ներդրված toString մեթոդը։

[].toString(); // ""

Դատարկ օբյեկտի վրա նույնպես տակից կատարվում է toString մեթոդի կանչը՝ ըստ ստանդարտում նկարագրված օբյեկտից String տիպին անուղղակի վերափոխման կանոնների։ Այդպես լինում է օրինակ, երբ մենք փորձում ենք որևէ օբյեկտ alert անել։ Քանի-որ alert ֆունկցիան աշխատում է միայն String տիպի հետ՝ ցանկացած այլ տիպ անուղղակի վերափոխում է String-ի։ Օբյեկտների դեպքում, երբ Symbol.toPrimitive կամ toString մեթոդների օգնությամբ ուղղակի տրված չի լինում, թե այդ փոխակերպումներն ինչ տրամաբանությամբ կատարվեն, անուղղակի փոխակերպման արդյունքը միշտ լինում է "[object Object]"։

({}).toString(); // "[object Object]"

Հետևաբար [] + {} արտահայտության մեջ մենք իրար ենք համակցում (concat) դատարկ տողն ու "[object Object]" տողը՝ արդյունքում ստացվում է "[object Object]"։

Այժմ դիտարկենք {} + [] === [] + {} հավասարությունը, որը վերադարձնում է true, թեև առանձին առանձին հավասարության ձախ և աջ կողմերը տարբեր արժեքներ են վերադարձնում։

Այստեղ արդեն ինտերպրետատորը {} + [] արտահայտության մեջ {}-ը դիտարկում է ոչ թե որպես կոդի դատարկ բլոկ, այլ որպես դատարկ օբյեկտ, և պլյուս օպերատորն ընդունելով, որպես համակցման օպերատոր, օբյեկտը անուղղակի վերափոխում է String տիպին, ստանալով "[object Object]", ապա նրան համակցում է դատարկ զանգվածի վերափոխումից ստացված դատարկ տողը։ Այսինքն հավասարության ստուգման ժամանակ ձախ և աջ ենթաարտահայտությունները ենթարկվում են լրիվ նույն տրամաբանությամբ փոխակերպման, և դրա համար էլ նույնականության օպերատորը վերադարձնում է true:

Ինտերպրետատորը կարծես {} + [] արտահայտությունը տակից խմբավորում է փակագծերի մեջ, որը և թույլ է տալիս ստանալ նման արդյունք։ Համեմատենք՝

{} + [] // 0
({} + []) // "[object Object]"

Չնայած միջավայրերի մեծ մասում մենք կունենանք նկարագրված արդյունքը՝ չի բացառվում, որ լինեն ոչ այնքան հայտնի բրաուզերներ որոնք վերադարձնեն բոլորովին այլ արդյունք: Այլ «տարօրինակություններ» կարող եք տեսնել այս կայքում։