JavaScript ლეგენდარული programming ენაა. შეიქმნა 1990-იანი წლების შუა ხანებში, მხოლოდ 10 დღეში, და დაიბადა...

დიახ, {} + [] = 0. არა, JavaScript არ არის გატეხილი

JavaScript ლეგენდარული programming ენაა. შეიქმნა 1990-იანი წლების შუა ხანებში, მხოლოდ 10 დღეში, და დაიბადა ბრაუზერების ომის დროს, როცა Netscape ცდილობდა Microsoft-თან დასრულებული დარტყმის მიყენებას. როგორც ყველამ ვიცით, ეს გეგმის მიხედვით ზუსტად არ გამოვიდა… დღეს ზოგს JavaScript უყვარს, ზოგს სძულს, ზოგი უბრალოდ ეცინება მას. მაგრამ სიმართლე ის არის, რომ თანამედროვე ვები მისი გარეშე უბრალოდ არ არსებობდა!

წლების განმავლობაში JavaScript უამრავ რევოლუციას გადაიტანა. დღევანდელი შესაძლებლობები თითქმის შეუდარებელია იმასთან, რისი გაკეთებაც ენას 1990-იან წლებში შეეძლო. არის მხოლოდ ერთი პატარა პრობლემა… ენაში ყოველი ცვლილება უნდა ითვალისწინებდეს იმ ფაქტს, რომ მილიონობით ვებსაიტი ჯერ კიდევ JavaScript-ის ძველ ქცევაზე დგას. და ზოგიერთი ეს საიტი, სავარაუდოდ, ამ კოდს დროის ბოლომდე გააგრძელებს.

ასე რომ, JavaScript-ის უცნაურობა, სავარაუდოდ, სამუდამოდ აქ დარჩება! 😂

საკმაოდ დიდი პაუზის შემდეგ გადავწყვიტე, დავაბრუნო ჩემი ოდესღაც პოპულარული სერია JS WTF. მიზეზი ძალიან მარტივია. ახლა iJS Munich-იდან ვბრუნდები და ფრანკფურტის აეროპორტში კიდევ ორი საათი მაქვს მოსაკლავი… (ნუ იკითხავ). ამდენი დრო არ მაქვს გარღვევის მომასწავებელი, ღრმად ტექნიკური სტატიის დასაწერად. მაგრამ, რა თქმა უნდა, საკმარისზე მეტი დროა, რომ აქ უბრალოდ კედელს ვუყურო.

ამიტომ ვნახოთ ერთ-ერთი ჩემი საყვარელი JavaScript კონსტრუქცია:

{} + [] // 0

დიახ, სწორად წაიკითხეთ. გარკვეულ პირობებში, თუ JavaScript-ში ცარიელობიექტს ცარიელ მასივს მივუმატებთ, ნულს მივიღებთ. და ეს სრულიად აზრიანია! 🤡

რა? რატომ ხდება ეს?

პირველი, რაც უნდა იცოდეთ, არის ის, რომ JavaScript ამ სიტუაციაში {}-ს არ აღიქვამს აუცილებლად როგორც ობიექტს! კონტექსტის მიხედვით, JavaScript parser გამოსახულებას ორ სრულიად განსხვავებულ რამედ შეიძლება აღიქვამდეს.

განვიხილოთ ეს კოდი კლასიკური JavaScript სკრიპტის დასაწყისში:

{} + []

JavaScript შეიძლება ეს ასე წაიკითხოს:

{}    // ცარიელი კოდის ბლოკი
+[]   // უნარული plus ცარიელ მასივზე (რა შეიძლება ავიდეს?)

სწორია. პირველი {} საერთოდ არ არის ობიექტის ლიტერალი. ეს უბრალოდ ცარიელი კოდის ბლოკია, რომელიც აბსოლუტურად არაფერს არ აკეთებს. ხოლო მეორე ნაწილი? აი, აქ იწყება გართობა.

ტიპების კონვერტაციის მაგია ✨

გამოსახულება:

+[]

წარმოადგენს კლასიკურ მაგალითს type coercion-ისა, იმ რამეებიდან, რითაც ჩვენი შესანიშნავი ენაა ცნობილი.

როგორც ალბათ იცით, JavaScript დინამიურად და სუსტად ტიპიზებული ენაა.

დინამიური ტიპიზაცია ნიშნავს, რომ ცვლადები მუდმივად არ არიან ერთ ტიპთან მიბმული. შეგიძლიათ ცვლადს რიცხვი მიანიჭოთ, შემდეგ კი სტრიქონით ჩაანაცვლოთ, და JavaScript არ უჩივლებს.

მაგრამ ეს ყველაფერი არ არის! JavaScript ასევე ასრულებს implicit ტიპების კონვერტაციას, რის გამოც მას ხშირად სუსტად ტიპიზებულ ენად მოიხსენიებენ. და ზოგჯერ ეს კონვერტაციები საკმაოდ… შემოქმედებით შედეგს იძლევა.

აიღეთ ჩვენი უნარული plus ოპერატორი:

+[]

განსხვავებით ბინარული + ოპერატორისგან, რომელსაც შეუძლია რიცხვების შეკრება ან სტრიქონების შეერთება, უნარული plus ცდილობს, თავისი ოპერანდი რიცხვით მნიშვნელობად გარდაქმნას. ამ შემთხვევაში ის არსებითად ასე მოქმედებს:

Number([])

დიახ, ვცდილობთ მასივის რიცხვად გარდაქმნას. და რატომ არა?

მაგრამ სანამ JavaScript ამ ოპერაციას დაასრულებს, მან სხვა კონვერტაცია უნდა შეასრულოს. მასივი ობიექტია, და JavaScript-მა ჯერ მისგან primitive მნიშვნელობა უნდა მიიღოს. ჩვეულებრივი ცარიელი მასივისთვის ეს პროცესი მის toString() მეთოდს იძახებს.

და ხომ იცით რა?

[].toString(); // ""

ცარიელი მასივი ცარიელ სტრიქონად იქცევა.

ამიტომ ჩვენი კონვერტაცია ფაქტობრივად ასე გამოიყურება:

Number([]);

// ჯერ მასივი primitive სტრიქონად იქცევა:
Number("");

// შემდეგ ცარიელი სტრიქონი ხდება:
0

რადგან:

Number("") // 0

და შესაბამისად:

+[] // 0

რაც ნიშნავს, რომ ჩვენი თავდაპირველი კონსტრუქცია:

{} + []

როცა ცარიელ ბლოკად და შემდეგ უნარულ plus-ად იკითხება, ფაქტობრივად ასე იქცევა:

{} // აბსოლუტურად არაფერს არ აკეთებს

+[] // 0

და ასე მივიღეთ ნული! მარტივი და აშკარა, არა? 🤡

სამართლიანად რომ ვთქვათ, ვერ წარმოვიდგენ სხვა ენას, სადაც ზუსტად ეს კონსტრუქცია ასეთ სრულიად ნორმალურად გამოიყურებულ შედეგს მოგვცემდა. მგონი, ჩვენს საყვარელ PHP-საც კი ამისთვის fatal error მოჰყვებოდა. თუმცა PHP-ს თავისი მომხიბვლელი სიურპრიზების კოლექცია აქვს, ამიტომ შეიძლება ძალიან მკაცრად არ უნდა განვსჯათ.

ერთი მნიშვნელოვანი დეტალი: ეს ქცევა კოდის parsing-ის გზაზეა დამოკიდებული. მაგალითად:

// კლასიკურ სკრიპტში, როგორც დამოუკიდებელი statement:
{} + [] // 0

// აიძულეთ JavaScript, რომ {} ობიექტად წაიკითხოს:
({} + []) // "[object Object]"

// ფუნქციის გამოძახების შიგნით:
console.log({} + []); // "[object Object]"

კარგი, მაგრამ რა არის [] + []?

ახლა, როცა type coercion გესმით, ეს ერთი აშკარა უნდა იყოს:

[] + [] // ""

აქ ბინარულ + ოპერატორთან გვაქვს საქმე. JavaScript ორივე მასივს primitive მნიშვნელობად გარდაქმნის. ხომ, რას მოელოდით ორი ცარიელი სტრიქონის შეკრებისას? 😀 არაფერი პლუს არაფერი უდრის არაფერს! მშვენიერია.

და ახლა, როგორც ჩანს, შეკრება კომუტატიურიც კი არ არის 😂

იგივე ლოგიკით სცადოთ კიდევ ერთი გამოსახულება:

[] + {} // "[object Object]"

მოიცადეთ.

ახლახანს ხომ დავადგინეთ, რომ:

{} + [] // 0

მაშ, რატომ გვაძლევს ოპერანდების ადგილების შეცვლა სრულიად სხვა შედეგს? როგორც ჩანს, შეკრების კომუტატიურობის თვისებაც კი არასავალდებულოა JavaScript-ში. xD

ტექნიკურად, აქ ყოველთვის არ ვასრულებთ მათემატიკურ შეკრებას და parser ამ გამოსახულებებს სხვადასხვანაირად ამუშავებს.

მაგრამ ტექნიკური დეტალებით კარგ ხუმრობას არ გავუფუჭებთ. აი, რა ხდება სინამდვილეში. ამ გამოსახულებაში:

[] + {}

ცარიელი მასივი ცარიელ სტრიქონად იქცევა:

[].toString() // ""

ხოლო ცარიელი ობიექტი? ჩვეულებრივ JavaScript ობიექტს საკუთარი მშვენიერი სტრიქონული წარმოდგენა აქვს:

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

ამიტომ JavaScript ფაქტობრივად ასრულებს:

"" + "[object Object]"

რაც გვაძლევს:

"[object Object]"

და სულ ესაა! მარტივი, როგორც ცეპის აშენება. ამ ფრაზას ვთარგმნი პოლონური გამოთქმიდან proste jak budowa cepa, რაც ნიშნავს, რომ რაღაც სასაცილოდ ადვილად გასაგებია. და მგონია, ეს JavaScript-ს ზუსტად უხდება. 😂

დასკვნითი აზრები

იმედი მაქვს, მოგეწონათ ეს პატარა მოგზაურობა JavaScript-ის საოცარ უცნაურობათა სამყაროში. და იმედი მაქვს, ამ სტატიის წაკითხვის შემდეგ უფრო მეტად დააფასებთ ამ ფენომენალური ენის სილამაზესა და ორიგინალურობას.

და საბოლოოდ, JavaScript 30 წელზე მეტია გვართობს, და მე ძლიერად ვვარაუდობ, რომ მას კიდევ ბევრი სიურპრიზი აქვს.

და საუკეთესო ნაწილი? ზემოთ მოყვანილი არც ერთი მაგალითი ბაგი არ არის. ისინი ყველა JavaScript-ის parsing წესების, type coercion-ისა და ენის უზარმაზარი ისტორიის, backward compatibility-ის შედეგებია. ასე რომ, მომდევნოჯერ, როცა ვინმე გეტყვით, რომ JavaScript გატეხილია, რადგან {} + [] შეიძლება იყოს 0, შეგიძლიათ თამამად აუხსნათ, რატომ არის ეს სრულიად აზრიანი.

შემდეგ ჯერზე გეტყვით, რატომ არის {} + {} = NaN… მაგრამ, სამწუხაროდ, დრო სწრაფად გადის, როცა სახალისოდ ვატარებ, და მალე უნდა მოვემზადო ჩემი ფრენისთვის!


P.S. თუ ჩემი კონტენტი მოგწონთ, თავისუფლად დამიკავშირდით ან დატოვეთ like ჩემს LinkedIn-ზე! ❤️

ორიგინალი: DEV Community · ავტორი: Sylwia Laskowska

ყველა სტატია
სიახლეები

მიიღეთ ახალი სტატიები

თვეში ერთხელ — რჩევები ვებ დიზაინზე, ახალი პროექტები და სტუდიის სიახლეები.

დარეკვა შეკვეთა