Gjør JavaScript-appene dine jevnere

Forfatter: Lewis Jackson
Opprettelsesdato: 5 Kan 2021
Oppdater Dato: 13 Kan 2024
Anonim
Gjør JavaScript-appene dine jevnere - Kreativ
Gjør JavaScript-appene dine jevnere - Kreativ

Denne artikkelen dukket først opp i nummer 231 av .net magazine - verdens mest solgte magasin for webdesignere og utviklere.

Moderne webapplikasjoner involverer ofte sanntidsanimasjon og interaktivitet - alt fra enkle overføringseffekter, Flash-lignende bannere og animasjoner implementert i HTML5 til komplette spill. JavaScript er raskere enn noensinne takket være moderne JIT-kompilatorer i nettlesere, men det er fortsatt et problem: søppelinnsamling (GC). Dette kan føre til pauser, stamming og dårlig ytelse, og ødelegge den jevne sanntidsopplevelsen.

Men hva er søppelinnsamling? JavaScript lar deg enkelt lage objekter. Det er imidlertid ingen måte å slette et gitt objekt (sletteoperatøren teller ikke: det er for å fjerne egenskaper på objekter). I stedet slettes objekter automatisk når de ikke lenger brukes.

Hvis du oppretter 1000 objekter, men bare beholder referanser til 500 av dem, vil nettleseren på et tidspunkt bestemme seg for å rydde opp. Det vil se at 500 ikke er referert hvor som helst og blir derfor ikke lenger brukt, så det renser dem. Noen nettlesere implementerer smarte teknikker for å gjøre samlingen så rask som mulig, for eksempel generasjonssamlere. Men hvis du oppretter mange objekter i JavaScript, må nettleseren før eller siden rydde opp: utførelsen er midlertidig stoppet, det bruker en stund på å finne ut hvilke objekter som fortsatt brukes, og deretter sletter resten.

Dette kan ta hundrevis av millisekunder, eller i noen tilfeller et helt sekund eller mer. En jevn animasjon må kjøre med 60 bilder per sekund, med bare 16ms per ramme, slik at dette kan skape en merkbar pause. Hvis det opprettes mange objekter, kan det hende at nettleseren må samle flere ganger i sekundet, noe som gjør animasjonen hakket. Hvis du spiller et actionspill i HTML5, er det få ting som er mer irriterende enn at en sjef dukker opp, du tar opp energi, tid til det perfekte trekket ... så tar nettleseren et øyeblikk, du savner øyeblikket ditt og taper spillet!

Den perfekte løsningen er å sørge for at ingenting tildeles i kodekjøringen for hver ramme. Men dette er overraskende vanskelig siden mange uskyldige uttalelser skaper søppel. Generelt er målet å holde graden av søppelproduksjon lav, slik at samlingene er sjeldne og ikke har mye å rydde opp i.

Den viktigste teknikken for å unngå søppeloppretting er å resirkulere gjenstander. Det betyr at i stedet for å lage mange objekter og kaste dem, kan du lage noen få objekter ved oppstart og bruke dem så lenge som mulig. Det betyr også å holde permanente referanser til gjenstander du ellers ville ha kastet. Cache er også viktig, siden flere gjenstander kan samles for resirkulering.


Å minimere antall opprettede objekter bidrar også til å forbedre JavaScript-ytelsen. Tildelinger og avfordelinger tar tid; å eliminere disse ved å bruke objekter på nytt, kan redusere den totale mengden arbeid nettleseren trenger å gjøre, og øke hastigheten på utførelsen. For å unngå oppretting av søppel, må vi forstå hvilke utsagn som tildeler objekter - det er åpenbart at den nye operatøren oppretter et nytt objekt. Imidlertid er det noen vanlige snarveier som også lager nye objekter du bør passe deg for:

{} // samme som: new Object () [] // same as: new Array () // oppretter også en ny funksjon! -funksjon () {...}

Ikke glem at nye tildelinger blir gjort selv når du spesifiserer egenskaper eller elementer, noe som er vanlig i JavaScript:

// Oppretter nytt objekt med en egenskap {"foo": "bar"} // Oppretter ny matrise med 3 elementer [1, 2, 3] // Oppretter 4 nye matriser! [[1, 2], [3, 4 ], [5, 6]]

Unngå alle disse i hvilken som helst kode som kalles ofte, spesielt hver ramme. I stedet prøver å lage objektet en gang og referere det permanent. En enkel endring å gjøre er å fjerne matriser, der å sette lengden til 0 er bedre enn å tildele en ny matrise:


// DÅRLIG: søppel gammel matrise, opprett // ny, og referer til thatmy_array = []; // BETTER: re-use the same array objectmy_array.length = 0;

Funksjoner kan også fange deg ut. Du vet kanskje at funksjoner også fungerer som objekter i JavaScript - men trodde du at de også teller som søppel? De fleste funksjoner er ikke et problem, siden du bare oppretter eller tilordner dem ved oppstart og bruker dem på nytt. Funksjoner som returnerer andre funksjoner (for eksempel i nedleggelse) kan imidlertid være mer av et problem enn du tror. Tenk på følgende eksempel ved hjelp av requestAnimationFrame for å kalle et spilles kryssfunksjon på et objekt (leverandørspesifikke utvidelser og løsninger er utelatt for klarhets skyld):

// DÅRLIG: opprett en helt ny funksjon // (som er søppeloppsamlet) // og kall det til neste framerequestAnimationFrame ((funksjon (selv) {returfunksjon () {selv.tikk ();};}) (dette )); // BEDRE: bruk den samme kryssfunksjonen på nytt! // Ved oppstart: this.tick_function = (function (self) {return function () {self.tick ();};}) (this); / / Når du ber om neste samtale: requestAnimationFrame (this.tick_function);

Ikke hørt om requestAnimationFrame før? Slå opp, det er bedre enn setInterval eller setTimeout for animasjoner!

Uansett, legg merke til hvordan det første eksemplet oppretter en ny funksjon for å kalle hvert kryss. Den andre lager en ved oppstart og bruker den på nytt, noe som unngår å kaste en funksjon hver ramme.

Det samme kan brukes på gjenstander. Du kan bruke et objekt på nytt ved å slette dets egenskaper og legge dem fra bunnen av igjen, i stedet for å tilordne et nytt objekt. Dette er imidlertid generelt ikke en god ide. Moderne nettlesere optimaliserer for objekter som er de samme. Når du sletter egenskaper, blir mange JavaScript-motorer deoptimalisert og begynner å kjøre langsommere.

Du bør absolutt unngå å slette egenskaper generelt. Det er imidlertid en kompromiss - hvis sletting av egenskaper sparer nok søppel til å redusere pauser, og det er verdt det for den lavere kjøringshastigheten når du får tilgang til objektet, kan du kanskje komme unna med det. Vær imidlertid oppmerksom på at der det er mulig, er det bedre å bruke et objekt på nytt med de samme egenskapene og ikke slette noe.


Når du går videre, blir ting litt vanskeligere. Mange biblioteksfunksjoner i JavaScript returnerer nye objekter, noe som kan gjøre det vanskelig å unngå tildelinger. For eksempel Array objektets skivefunksjon returnerer en ny matrise basert på en del av den gamle matrisen. Hvis du ringer til skive, foretas en tildeling, og det er ingenting du kan gjøre med det bortsett fra for å unngå å ringe det. Du kan også omskrive en tildelingsfri versjon i JavaScript, men du kan ende opp med å finne opp hjulet på nytt, og implementere mange vanlige JavaScript-funksjoner på nytt. Det kan være verdt å vurdere for hotspots for søppeloppretting, skjønt.

Som alltid er Mozilla Developer Network (MDN) et flott sted å slå opp JavaScript-biblioteksfunksjonene. For eksempel MDN-dokumentasjonen på Array.skive stater som "skjære endrer ikke den opprinnelige matrisen, men returnerer en ny ‘one level deep’ kopi ”. Dette er vårt hint om at funksjonen returnerer en kopi (en ny matrise) i stedet for å endre originalen; derfor bør vi unngå det når vi minimerer søppel. Ved å slå opp de andre funksjonene i kodebanen per ramme, kan du også finne ut om de lager nye objekter eller endrer på plass. De kan være enkle å blande sammen. Hvis det er mulig, kan det å redusere oppretting av søppel erstattes av objektskapende funksjoner med funksjoner som endrer objektet på plass. Tenk på eksemplet nedenfor:

// Intensjon: reduser matrisen til elementer // 1, 2 og 3.// DÅRLIG: anropsdel: ny matrise opprettes // og den gamle er garbagedarr = arr.skive (1, 4). til 4, så skift // (fjern første element) // Oppretter ikke noen nye objekter! arr.length = 4; arr.shift ();

Cacher kan også hjelpe deg med å resirkulere mange gjenstander. For eksempel kan en partikkeleffekt lagre en rekke partikkelobjekter. Når partikler blir ødelagt, kan du bare fjerne dem fra matrisen, der de blir søppel og senere blir ryddet opp. Et smartere valg er resirkulering: å flytte dem til et 'dødt partikelsystem'.

Når du lager nye partikler, hvis 'døde partikler' har noen gjenstander, pop () en gjenstand og tilbakestill den til sin opprinnelige tilstand, og flytt den deretter tilbake til den aktive partiklene. Hvis 'døde partikler array' er tom, har du ikke noe annet valg enn å lage en ny Partikkel (), men det er OK; du må uansett opprette nye partikler ved oppstart (når matrisen først er tom), og fra da av blir de resirkulert.

Lignende prinsipper kan brukes i hele JavaScript. For eksempel kan det hende du har en bunke med objekter, implementert ved å skyve og poppe til en matrise. Hvis du popper fra matrisen vil søppel gjenstanden, kan du vurdere å cache den. Du kan også bruke et heltall som peker på 'toppindeksen' i stabelen, og øke og redusere det i stedet for å skyve og poppe, resirkulere objekter mens du går. Stabler kan også være en viktig måte å omgå med å sende nye objekter i rekursive funksjoner, hvor like nyttige som {} syntaksen er, kan det fremdeles skape mye søppel.

Noen av de verste lovbryterne for GC-ytelse i JavaScript er vektorobjekter (for eksempel et objekt som inneholder en x- og y-koordinat); JS-implementeringer av Box2D-fysikk lider ofte av dette problemet. Biblioteker designet rundt slike objekter kan lage tusenvis av vektorobjekter per sekund, ofte uten å resirkulere dem - eller krever spesielle anrop for å 'frigjøre' dem, noe som kan være vanskelig å komme akkurat på de riktige stedene.

Ofte viser applikasjoner laget med biblioteker som disse de verste søppelpausene. Det er langt bedre å ha funksjoner som fungerer på hver koordinat separat - for eksempel getX () og getY (), som returnerer enkle tall, snarere enn getPosition (), som returnerer en ny vector2 (…). Når du bestemmer deg mellom biblioteker, vil du sannsynligvis unngå alle som bruker vektorobjekter som det for best ytelse. Vektorobjekter er virkelig praktiske, og kan gjøre koden din ryddigere og enklere, men JavaScript-språkets design gjør det vanskelig å bruke dem uten å skape store mengder søppel - noe som gjør spillet ditt eller animasjonen hakkete!

Så å gjøre endringer for å redusere GC-overhead kan dessverre gjøre koden din mer komplisert og vanskelig å følge. Men med forsiktighet er det fullt mulig å lage JavaScript i sanntid som vil gjøre spill, effekter eller animasjoner jevnere og mer responsive enn resten.

Nettstedsvalg
Sandkriger: hvorfor designere bør bry seg om at verdens sand forsvinner
Lese

Sandkriger: hvorfor designere bør bry seg om at verdens sand forsvinner

Planet andre erver er under alvorlig tru el. Drevet av en verden om pennende byggeboom og økende urbani ering eroderer aggre iv anddrift trender, ødelegger infra truktur, for tyrrer øko...
20 ikoniske merker - og hvorfor de fungerer
Lese

20 ikoniske merker - og hvorfor de fungerer

'Iconic' er et tort merke. Men det er mer med et ikoni k merke enn bare logoen. Det hjelper ab olutt, men du vil ofte oppdage at de tør te merkene ikke er de om har de be te logoene.Logo ...
26 WordPress-porteføljetemaer av topp kvalitet
Lese

26 WordPress-porteføljetemaer av topp kvalitet

WordPre -porteføljetemaer gjør det enkelt å lage en profe jonell og vært effektiv portefølje ide, om er enkel å redigere og navigere om du vil, på en lignende må...