JavaScript
sproget, browsere kører
Hvad det er
JavaScript er et dynamisk typet programmeringssprog, standardiseret under navnet ECMAScript, som enhver webbrowser kan afvikle, og som også kører på servere og byggemaskiner gennem afviklingsmiljøer som Node.js. I daglig tale hedder det JS. Standardens årlige udgaver hedder ES2015, ES2024 og så videre, de gamle numre (ES5, ES6) høres stadig, og »vanilla JavaScript« betyder sproget uden et framework ovenpå. Bortset fra navnet og de krøllede parenteser har det meget lidt med Java at gøre.
Brendan Eich skrev den første version hos Netscape, og den kom med i browseren Navigator 2.0. Ecma International vedtog første udgave af standarden, ECMA-262, i juni 1997, og organisationens komité TC39 har udsendt en ny udgave hvert år siden ECMAScript 2016. Et forslag kommer først med i specifikationen på trin 4 i komitéens proces, og det trin kræver implementeringer, der allerede er sendt ud. Derfor er teksten og motorerne sjældent uenige ret længe.
Sproget selv siger meget lidt om verden udenfor. ECMA-262 definerer syntaksen, typerne, objekter, promises og moduler og overlader input og output til det miljø, der afvikler sproget, og som standarden kalder værten. I en browser er værten webplatformen: dokumentet, hændelser, timere og netværkskald, som for det meste er beskrevet i HTML Standard og dens søsterstandarder. I Node.js stiller værten i stedet filer, sockets og processer til rådighed. Den deling forklarer en klassisk overraskelse: kode kan være fuldt gyldig JavaScript og alligevel gå i stykker, når den flytter fra en side til en server, for sproget flyttede med, mens værten blev hjemme.
I browseren deler en sides scripts én hovedtråd med layout, tegning og input. En opgave kører
færdig, før browseren kan reagere på det næste klik. Promises og async-funktioner holder
ventetiden væk fra tråden, men beregninger bliver på den, medmindre de flyttes over i en Web Worker.
Tal er IEEE 754-værdier med dobbelt præcision, så hele tal er kun eksakte op til 2^53 − 1, og alt
derover kræver BigInt.
Hvorfor det betyder noget
Den egentlige beslutning handler om, hvor meget af det man sender til dem, der læser sitet. JavaScript er det sprog, der får en side til at reagere uden en tur forbi serveren, og hver kilobyte af det skal hentes, parses, kompileres og afvikles på en enhed, man ikke selv råder over. Et bundt, der føles øjeblikkeligt på en udviklers bærbare, kan holde en mellemklassetelefon beskæftiget i flere sekunder, og regningen betaler læseren med batteri og tålmodighed.
Script er også der, hvor en side står mest åben. Alt, hvad der bliver sprøjtet ind i den, kører med sidens fulde rettigheder: det kan læse, hvad læseren taster, og sende det hvor som helst, siden selv må sende hen. Det er den risiko, Content Security Policy findes for at inddæmme, og en side helt uden script giver angriberen intet at arbejde med. Pakkeøkosystemet åbner en anden flanke. Et typisk frontendbyg trækker hundredvis af transitive afhængigheder med sig, og hver eneste af dem er kode, der kører med de samme rettigheder som ens egen.
Den anden side af regnestykket er lige så virkelig. Når det samme sprog kører på begge sider af et system, kan browseren og serveren dele en valideringsregel eller en dataform, og nogle produkter har brug for meget script: offlinetilstand, træk og slip, kort, samarbejde i realtid. Det nyttige spørgsmål stilles derfor funktion for funktion: skal det her ske i læserens browser, uden at en server bliver spurgt? Lyder svaret nej, så lad serveren eller bygningen lave HTML’en, og send den.
Det beslægtede valg står mellem JavaScript og TypeScript. TypeScript tilføjer en typetjekker og et byggetrin og kompilerer tilbage til almindelig JavaScript. I en kodebase, som flere mennesker ændrer hver uge, tjener tjekkeren som regel sig selv hjem. Til en forbedring på hundrede linjer på en statisk side rækker JavaScript og en streng linter.
Sådan ser det ud i praksis
Dette site sender næsten ingen JavaScript til sine læsere og afvikler en hel del af det, når det bygges. Tre sider viser begge halvdele.
Det offentlige site er et statisk byg på tre sprog uden en backend, der kører, og browseren modtager ét eneste førstepartsscript. Scriptet er en afgrænset undtagelse til én funktion, et porteføljefilter, hvis kontroller er skjult, indtil scriptet har kørt. Resten af sitet, 108 skabelonfiler med 53 partials og 18 shortcodes, gør sit arbejde, mens sitet bygges. Siden skriver også prisen for det valg ned: alt interaktivt skal løses ved bygningstid, og derfor blev et søgeindeks prissat og parkeret i stedet for at blive sendt ud.
Det samme indhold udgives over Gemini og Gopher, to protokoller, hvor et script slet ikke har nogen plads. Bygget frembringer 777 Gemini-dokumenter og 777 Gopher-menuer fra den samme kilde, og en måling bekræftede, at HTML’en forblev uændret ned til sidste byte. De to alternativer lægger omkring tolv procent oven i HTML’ens vægt på disken. Sitets argument er, at et dokument klarer sig uden en megabyte script for at blive læst, og et format, der slet ikke kan rumme et script, er den mest ligefremme måde at vise det på.
Det meste JavaScript her bor i værktøjerne. Sitets kvalitetsgate er toogtredive scripts og 10.242 linjer JavaScript, den største kodemængde i repositoryet, og i en periode var det den eneste kode, som hverken blev formateret, lintet eller typetjekket. Arbejdet med at få den under en formatter og en linter begyndte med at tilpasse formatteren til koden: repositoryets standard på fire mellemrum ville have omskrevet 2.173 linjer af den største tjekker, og en overstyring til to mellemrum for de filer efterlod 38 linjers ægte drift. Derefter rejste linteren 13 fund, og alle 13 blev rettet uden en eneste undertrykkelse. En fejl i en tjekker viser sig som et tjek, der består, når det burde slå fejl, og derfor får gatens egen kode den behandling, den selv giver alt andet.
Hvor det går galt
Fejlene herunder følger af, hvordan sproget er skruet sammen, og hver af dem har en kendt løsning.
Siden er tegnet, men reagerer først på tryk et sekund senere. En lang opgave, som at hydrere et stort bundt, parse et stort JSON-svar eller sortere tusindvis af rækker, optager den ene tråd, der håndterer input. Send mindre kode til den første interaktion ved at dele bundtet op pr. rute og udskyde resten, del langt arbejde op i bidder, der giver kontrollen tilbage til event-loopet, og flyt tunge beregninger over i en Web Worker. Mål på en langsom telefon frem for på den bærbare, koden blev skrevet på.
Priser og id’er kommer ud en anelse forkerte. Hvert tal er et binært flydende kommatal, så decimalbrøker afrundes, og meget store heltal falder sammen med deres naboer. En 64-bit databasenøgle sendt som et JSON-tal er det typiske offer:
0.1 + 0.2 === 0.3; // false
Number.MAX_SAFE_INTEGER; // 9007199254740991
9007199254740993 === 9007199254740992; // true
Hold beløb i hele mindsteenheder som øre, send store id’er som strenge, og brug BigInt, hvor
regnestykket skal være eksakt.
En sammenligning lukker den forkerte værdi igennem. Operatoren == konverterer sine operander,
før den sammenligner, så "" == 0 og "0" == false er begge sande. Brug === overalt, og lad
linterens regel eqeqeq håndhæve det, så en konvertering kun sker, når koden udtrykkeligt beder om
den.
Tekst fra en bruger ender med at køre som kode. At tildele utroværdig tekst til innerHTML, at
bygge markup ved at sætte strenge sammen eller at give input videre til eval gør data til script.
Skriv tekst med textContent, send en Content Security Policy, der kun lukker scripts ind via nonce
eller hash, og hold listen over afhængigheder kort og fastlåst i en lockfile, så hver opdatering
bliver læst, før den kommer ind.
Kode, der virker i browseren, går ned på serveren. Sproget flyttede, værten blev: window og
document findes kun i en browser, og Node.js læser en fil som CommonJS eller som ES-modul
afhængigt af indstillinger, browseren aldrig ser. Hold værtsspecifikke kald ude i kanterne, sæt
"type": "module" eksplicit i package.json, og kør hver del af koden i det miljø, den faktisk
skal køre i.
Læs mere
- ECMAScript Language Specification — det levende udkast til ECMA-262, den tekst alle motorer implementerer, opdateret efterhånden som forslag når trin 4.
- The TC39 Process — de trin, et forslag går igennem, før det bliver en del af sproget, og hvad hvert trin kræver.
- ECMA-262 hos Ecma International — de udgivne årlige udgaver, til når et projekt skal navngive den version, det sigter efter.
- HTML Standard: event loops — hvordan en browser planlægger opgaver, mikroopgaver og rendering omkring en sides scripts.
- HTML Standard: the script element
— klassiske scripts og modulscripts, og hvordan
asyncogdeferændrer, hvornår de kører.