“Twas a long time ago,
longer now than it seems,
in a place that perhaps you’ve seen in your dreams.
For the story that you are about to be told.”
– Danny Elfman, Nightmare Before Christmas
Nadahnuti Halloweenom koji je upravo prošao, evo nekoliko zastrašujućih JavaScript priča. Ali nemojte se previše bojati. Kao u najboljem Scooby-Doo crtiću, u svakom primjeru namjeravam pokazati da se zapravo ne bavimo čudovištima, već samo ljudima koji zloupotrebljavaju JavaScript – bilo zbog nepoznavanja jezika ili zbog nepažnje.
Većina ljudi počne koristiti JavaScript slučajno – to jest, osobno nisam upoznao nikoga tko ga je namjerno proučavao. Kao što su drugi naletjeli na slične uratke i ja sam ostao šokiran raznim primjerima i idejama. Postoje stotine memova o JavaScriptu, ali većina je samo proizvod općeg nedostatka znanja o samom jeziku. Lako napisati kod jer je prilično sličan ostalim programskim jezicima, no ipak ljudi pretpostavljaju da ga dobro znaju. Ali kako to često biva, varaju se.
Poanta JavaScript-a je da bi trebao raditi i biti izuzetno otporan na kvarove, ali može iznenaditi što se nalazi u pozadini. Započnimo s laganim primjerom.
Operator zbrajanja
> ‘1’ + 1 // -> “11”
> ‘1’ – 1 // -> 0
> ‘1’/’1′ // -> 1
> ‘1’*’1′ // -> 1
Operator + je preopterećen. Ako su lijevi ili desni operand tipa string, drugi se pretvara u string i rezultat je spajanje nizova.
Ostali operateri nisu preopterećeni, ali JavaScript nam u tom slučaju pomaže s pretpostavkom da radimo s brojevima i pokušava ih pretvoriti u brojeve.
Banana
“b” + “a” + +”a” + “a”; // -> ‘baNaNa’
Ajmo se malo našaliti na račun JavaScript-a. Zbog prednosti operatora, gornji izraz se procjenjuje kao
“b”+”a”+(+”a”)+”a”
Budući da unarni + pretvara desnu stranu u broj, a ‘a’ je nevažeći broj, rezultat je NaN (ne broj), a nakon toga slijedi osnovna operacija povezivanje stringova.
Slobodna jednakost
> true == 2; // -> false
> true == 1; // -> true
if(2) {} // -> enters into block
U JavaScriptu, prilična vrijednost je vrijednost koja se pretvara u true kada se procjenjuje u logičkom kontekstu. Sve su vrijednosti istinite, osim ako nisu jedna od sljedećih sedam: false, 0, ”, ““, null, undefined, NaN.
Ako su lijeva i desna strana iste vrste, vrši se stroga usporedba, što nije slučaj u gornjim primjerima. Ako je bilo koja od strana Boolean, a druga broj, Booleova vrijednost se pretvara u broj, a zatim se vrši usporedba. True se pretvara u 1, a rezultat je očekivan. Za više pojedinosti o redoslijedu jednakosti pogledajte na ECMA documentataciji. Dakle, rješenje je ili biti svjestan ili upotrijebiti strogu usporedbu – to jest === koji će vratiti false ako su različiti tipovi.
U gornjem slučaju s uvjetom if, vrijednost 2 moramo pretvoriti u Boolean. Budući da ga nema na našoj false listi, onda je istinit, pa ga ocjenjujemo kao true.
NaN
NaN === NaN // -> false
Polazeći od IEEE745 definicije brojeva s pomičnom zarezom, NaN u usporedbi s bilo čime je NaN.
parseInt
parseInt(“Ekobit”) // -> NaN
> parseInt(“Ekobit”, 16) // -> 14
> parseInt(“Ekobit”, 36) // -> 881253461
Parsiranjem int-a raščlanit ćemo znak po znak sve dok ne pogodimo znak koji nije definiran osnovom, a to je prema zadanim postavkama 10, pa odmah neće uspjeti na „Ekobitu“. U bazi 16, ‘E’ je 14, a ‘k’ nije definiran pa je krajnji rezultat 14, a u bazi 36 koristimo sve znamenke i slova engleske abecede, pa sve parsiramo.
parseInt(0.000001) // -> 0
> parseInt(0.0000001) // -> 1
> parseInt(1 / 1999999) // -> 5
Parsiranje int-a funkcionira raščlanjivanjem stringa, pa float prvo treba pretvoriti u string. 0,000001 se pretvara u ‘0,000001’ i, kao što je gore objašnjeno, parsiranje se vrši do prvog nepoznatog znaka koji je ‘.’, a krajnji rezultat parseInt je 0. Ako dodamo još jednu nulu, konverzija float to string daje ‘ 1e-7 ‘ i prema istoj logici parsiranja do nepoznatog znaka, rezultat je 1. U slučaju dijeljenja rezultat je 5,00 … e-7, tako da je rješenje 5.
Comparison
1 < 2 < 3 // -> true
> 3 > 2 > 1 // -> false
Ovo je opet čudan komad koda za pisanje, a rezultat je jednostavno iskorištavanje jezika koji piše na tjednoj bazi. U oba primjera, prva se usporedba procjenjuje kao true, a zatim se istina baca na 1 prije druge evaluacije, tako da prvi redak vraća true, a drugi, false.
Maksimum je mali, a minimum je velik
Math.min() > Math.max() // -> true
I max i min su funkcije koje primaju niz i vraćaju najveći i najmanji element. U pseudokodu, kôd za min je:
> smallest = Infinity;
> foreach element in array
> if element smaller than smallest
> smallest = element
> return smallest
Kôd se završava bez pokretanja petlje, pa je najmanja vraćena vrijednost Infinity. Slično tome, za max, vraćena vrijednost je -Infinity.
Sortiranje
> [10, 1, 7].sort() // -> [1, 10, 7]
Zadana sortiranja se temelje na stringu. Nemam puno više za reći od toga. Ako želite razvrstati integer-e, trebali biste napisati vlastitu funkciju.
> [10, 1, 7].sort((a, b) => a – b) // -> [1, 7, 10]
Minimalna vrijednost
> Number.MIN_VALUE < 0 // -> false
Napokon, nešto čudno i krivo? Ne baš, samo pogrešno imenovano svojstvo. Svojstvo bi trebalo biti MIN_POSITIVE_VALUE. Mnogi programi imaju bugove koji se odnose na ovu vrijednost. To je samo najmanja pozitivna vrijednost koja se može predstaviti unutar preciznosti float-a – to jest 5e-324. Slično tome, MAX_VALUE je maksimalna pozitivna vrijednost, ali u ovom slučaju, logično i semantički djeluje, jer daje najveći pozitivan broj koji je jednak najvećem broju.
Malo zabave na kraju
Možemo li napisati sljedeći JavaScript kod?
> (a == 1 && a == 2 && a == 3) // -> true
Naravno!
> const a = {
> num: 0,
> valueOf: function() {
> return this.num + 1
> }
> };
Primijetite da koristimo slobodnu jednakost: == umjesto ===. JavaScript će pokušati oblikovati naš objekt u broj kako bi ga usporedio. Dakle, kada to prisilno radi koristi funkciju valueOf.
Ukratko
Proširio bih popularni citat Bjarnea Stroustrupa, „C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off!” i dodao još jedan dio, koji bi bio, ” and JavaScript makes shooting yourself in the foot seem trivial because if you don’t carefully check whether ‘the gun’ is loaded and you end up shooting yourself, you’ll lose the entire lower part of your body. And as if that weren’t enough, you won’t notice what’s happened until a few days after the accident when someone asks you why you’re crawling!”
Neka vas novi framework ne zavara s typecript-om, CoffeeScripta ili nekim drugim otmjenim uređivačem teksta. Na kraju, za web preglednik sve je samo JavaScript i gricnut će vas ako ga zloupotrijebite.