ES5 til ESNæste - her er alle funktioner, der er tilføjet til JavaScript siden 2015

Jeg skrev denne artikel for at hjælpe dig med at bevæge dig fra før-ES6-viden om JavaScript og hurtigt komme dig op med de nyeste fremskridt på sproget.

JavaScript i dag er i den privilegerede position at være det eneste sprog, der kan køre indfødte i browseren, og er stærkt integreret og optimeret til det.

Fremtiden for JavaScript bliver strålende. At følge med ændringerne burde ikke være sværere, end det allerede er, og mit mål her er at give dig en hurtig, men alligevel omfattende oversigt over de nye ting, vi har til rådighed.

Klik her for at få en PDF / ePub / Mobi-version af dette indlæg til at læse offline

Indholdsfortegnelse

Introduktion til ECMAScript

ES2015

  • lad og konst
  • Pilefunktioner
  • Klasser
  • Standardparametre
  • Skabelon bogstaver
  • Destruktureringsopgaver
  • Forbedrede objektlitterære
  • For-of-loop
  • Promises
  • moduler
  • Nye strengmetoder
  • Nye objektmetoder
  • Spredningsoperatøren
  • Sæt
  • Kort
  • generatorer

ES2016

  • Array.prototype.includes ()
  • Eksponentiering Operator

ES2017

  • Streng polstring
  • Object.values ​​()
  • Object.entries ()
  • Object.getOwnPropertyDescriptors ()
  • Efterfølgende kommaer
  • Delt hukommelse og atomer

ES2018

  • Hvil / spred egenskaber
  • Asynkron iteration
  • Promise.prototype.finally ()
  • Regelmæssige udtryk forbedringer

ESNext

  • Array.prototype. {Flad, flatMap}
  • Valgfri fangstbinding
  • Object.fromEntries ()
  • String.prototype. {TrimStart, trimEnd}
  • Symbol.prototype.description
  • JSON forbedringer
  • Velformet JSON.stringify ()
  • Function.prototype.toString ()

Introduktion til ECMAScript

Hver gang du læser om JavaScript, vil du uundgåeligt se et af disse udtryk: ES3, ES5, ES6, ES7, ES8, ES2015, ES2016, ES2017, ECMAScript 2017, ECMAScript 2016, ECMAScript 2015 ... hvad betyder de?

De henviser alle til en standard, kaldet ECMAScript.

ECMAScript er den standard, hvorpå JavaScript er baseret, og det er ofte forkortet til ES.

Foruden JavaScript implementerer (redigeres) andre sprog ECMAScript, herunder:

  • ActionScript (Flash-scriptsproget), som mister popularitet, da Flash officielt ophører i 2020
  • JScript (Microsoft scripting-dialekt), da JavaScript på det tidspunkt kun blev understøttet af Netscape og browserkrigene var på sit højeste, måtte Microsoft bygge sin egen version til Internet Explorer

men selvfølgelig er JavaScript den mest populære og mest anvendte implementering af ES.

Hvorfor dette underlige navn? Ecma International er en schweizisk standardforening, der har ansvaret for at definere internationale standarder.

Da JavaScript blev oprettet, blev det præsenteret af Netscape og Sun Microsystems for Ecma, og de gav det navnet ECMA-262 alias ECMAScript.

Denne pressemeddelelse fra Netscape og Sun Microsystems (producenten af ​​Java) kan muligvis hjælpe med at finde ud af navnevalget, som kan indeholde juridiske og branding-problemer fra Microsoft, som var i udvalget, ifølge Wikipedia.

Efter IE9 stoppede Microsoft branding af sin ES-support i browsere som JScript og begyndte at kalde det JavaScript (i det mindste kunne jeg ikke finde referencer til det mere).

Så fra og med 201x er det eneste populære sprog, der understøtter ECMAScript-spec, JavaScript.

Nuværende ECMAScript-version

Den aktuelle ECMAScript-version er ES2018.

Den blev frigivet i juni 2018.

Hvad er TC39

TC39 er det udvalg, der udvikler JavaScript.

Medlemmerne af TC39 er virksomheder, der er involveret i JavaScript- og browserudbydere, herunder Mozilla, Google, Facebook, Apple, Microsoft, Intel, PayPal, SalesForce og andre.

Hvert standardversionsforslag skal gennemgå forskellige faser, som er forklaret her.

ES-versioner

Jeg fandt det forundrende, hvorfor der undertiden henvises til en ES-version med udgavenummer og undertiden efter år, og jeg er forvirret over, at året ved en tilfældighed er -1 på tallet, hvilket tilføjer den generelle forvirring omkring JS / ES

Før ES2015 blev ECMAScript-specifikationer ofte kaldt op efter deres udgave. Så ES5 er det officielle navn på ECMAScript-specifikationsopdateringen, der blev offentliggjort i 2009.

Hvorfor sker dette? Under processen, der førte til ES2015, blev navnet ændret fra ES6 til ES2015, men da dette blev gjort sent, omtalte folk det stadig som ES6, og samfundet har ikke efterladt udgaven med at navngive sig - verden kalder stadig ES-udgivelser af udgave nummer.

Denne tabel skal rydde lidt op:

Lad os dykke ned i de specifikke funktioner, der er tilføjet til JavaScript siden ES5. Lad os starte med ES2015-funktionerne.

lad og konst

Indtil ES2015 var var den eneste konstruktion til rådighed til at definere variabler.

var a = 0

Hvis du glemmer at tilføje var, tildeles du en værdi til en ikke-erklæret variabel, og resultaterne kan variere.

I moderne miljøer, med streng tilstand aktiveret, får du en fejl. I ældre miljøer (eller med streng tilstand deaktiveret) vil dette initialisere variablen og tildele den til det globale objekt.

Hvis du ikke initialiserer variablen, når du erklærer den, vil den have den udefinerede værdi, indtil du tildeler en værdi til den.

var a // typeof a === 'undefined'

Du kan gentegne variablen mange gange og tilsidesætte den:

var a = 1
var a = 2

Du kan også erklære flere variabler på én gang i den samme sætning:

var a = 1, b = 2

Omfanget er den del af koden, hvor variablen er synlig.

En variabel, der er initialiseret med var uden for enhver funktion, tildeles det globale objekt, har et globalt omfang og er synligt overalt. En variabel, der er initialiseret med var inde i en funktion, er tildelt den funktion, den er lokal og er kun synlig inde i den, ligesom en funktionsparameter.

Enhver variabel defineret i en funktion med samme navn som en global variabel har forrang for den globale variabel og skygger den.

Det er vigtigt at forstå, at en blok (identificeret af et par krøllede seler) ikke definerer et nyt omfang. Et nyt omfang oprettes kun, når en funktion oprettes, fordi var ikke har blokomfang, men funktionsomfang.

Inde i en funktion er enhver variabel, der er defineret i den, synlig i hele funktionskoden, selvom variablen er erklæret i slutningen af ​​funktionen, kan der stadig henvises til det i begyndelsen, fordi JavaScript, før koden udføres, faktisk flytter alle variabler ovenpå (noget der kaldes hejsning). For at undgå forvirring skal du altid angive variabler i begyndelsen af ​​en funktion.

Brug af let

let er en ny funktion introduceret i ES2015, og det er i det væsentlige en blok scoped version af var. Dets omfang er begrænset til blokken, udsagnet eller udtrykket, hvor det er defineret, og alle de indvendige blokke.

Moderne JavaScript-udviklere vælger muligvis kun at bruge let og helt kassere brugen af ​​var.

Hvis lad synes at være et uklart udtryk, skal du bare læse let color = 'rød', så lad farven være rød og det hele giver meget mere mening

Definition af udlejning uden for nogen funktion - i modsætning til var - skaber ikke en global variabel.

Brug af const

Variabler, der er erklæret med var eller let, kan ændres senere i programmet og tildeles igen. Når en const er initialiseret, kan dens værdi aldrig ændres igen, og den kan ikke tildeles igen til en anden værdi.

const a = 'test'

Vi kan ikke tildele en anden bogstavelig størrelse til a const. Vi kan dog mutere a, hvis det er et objekt, der indeholder metoder, der muterer dens indhold.

const giver ikke uforanderlighed, bare sørger for, at referencen ikke kan ændres.

const har blokeringsomfang, det samme som let.

Moderne JavaScript-udviklere vælger muligvis altid at bruge const til variabler, der ikke behøver at blive tildelt senere i programmet, fordi vi altid skal bruge den enkleste konstruktion, der er tilgængelig for at undgå at begå fejl på vejen.

Pilefunktioner

Pilefunktioner, siden deres introduktion, ændrede for evigt hvordan JavaScript-kode ser ud (og fungerer).

Efter min mening var denne ændring så velkommen, at du nu sjældent ser brugen af ​​funktionsnøgleordet i moderne kodebaser. Selvom det stadig har dens anvendelse.

Visuelt er det en enkel og velkommen ændring, som giver dig mulighed for at skrive funktioner med en kortere syntaks fra:

const myFunction = funktion () {
  // ...
}

til

const myFunction = () => {
  // ...
}

Hvis funktionskroppen kun indeholder en enkelt sætning, kan du udelade parenteserne og skrive alle på en enkelt linje:

const myFunction = () => doSomething ()

Parametre sendes i parenteserne:

const myFunction = (param1, param2) => doSomething (param1, param2)

Hvis du har en (og bare en) parameter, kan du udelade parenteserne fuldstændigt:

const myFunction = param => doSomething (param)

Takket være denne korte syntaks tilskynder pilefunktioner til brug af små funktioner.

Implicit afkast

Pilfunktioner giver dig mulighed for at have en implicit returnering: værdier returneres uden at skulle bruge return søgeordet.

Det fungerer, når der er en sætning på én linje i funktionsorganet:

const myFunction = () => 'test'
myFunction () // 'test'

Et andet eksempel, når du returnerer et objekt, skal du huske at indpakke de krøllede konsoller i parentes for at undgå, at det betragtes som indpakningsfunktionens kropsbeslag:

const myFunction = () => ({value: 'test'})
myFunction () // {value: 'test'}

Sådan fungerer det i pilens funktioner

dette er et koncept, der kan være kompliceret at forstå, da det varierer meget afhængigt af konteksten og også varierer afhængigt af JavaScript-tilstanden (streng tilstand eller ej).

Det er vigtigt at afklare dette koncept, fordi pilefunktioner opfører sig meget forskelligt sammenlignet med almindelige funktioner.

Når det defineres som en metode til et objekt, refererer dette i en regelmæssig funktion til objektet, så du kan gøre:

const bil = {
  model: 'Fiesta',
  producent: 'Ford',
  fuldnavn: funktion () {
    returner `$ {dette.producent} $ {dette.model}`
  }
}

ringer car.fullName () returnerer "Ford Fiesta".

Dette omfang med pilefunktioner erves fra udførelseskonteksten. En pilefunktion binder overhovedet ikke dette, så dens værdi bliver slået op i opkaldsstakken, så i denne kode fungerer car.fullName () ikke og returnerer strengen "undefined undefined":

const bil = {
  model: 'Fiesta',
  producent: 'Ford',
  fuldnavn: () => {
    returner `$ {dette.producent} $ {dette.model}`
  }
}

På grund af dette er pilefunktioner ikke egnede som objektmetoder.

Pilefunktioner kan heller ikke bruges som konstruktører, når en øjeblikkelig genstand af et objekt hæver en TypeError.

Det er her regelmæssige funktioner skal bruges i stedet, når dynamisk kontekst ikke er nødvendig.

Dette er også et problem, når du håndterer begivenheder. DOM Event lyttere indstiller dette til at være målelementet, og hvis du stoler på dette i en event handler, er en regelmæssig funktion nødvendig:

const link = document.querySelector ('# link')
link.addEventListener ('klik', () => {
  // dette === vindue
})
const link = document.querySelector ('# link')
link.addEventListener ('klik', funktion () {
  // dette === link
})

Klasser

JavaScript har en ganske ualmindelig måde at implementere arv på: prototype arv. Prototypisk arv er, efter min mening stor, i modsætning til de fleste andre populære programmeringssprogs implementering af arv, som er klassebaseret.

Folk der kommer fra Java eller Python eller andre sprog havde svært ved at forstå vanskelighederne med prototype arv, så ECMAScript-udvalget besluttede at drysse syntaktisk sukker på toppen af ​​prototypisk arv, så det minder om, hvordan klassebaseret arv fungerer i andre populære implementeringer.

Dette er vigtigt: JavaScript under hætten er stadig den samme, og du kan få adgang til en prototype på den sædvanlige måde.

En klassedefinition

Sådan ser en klasse ud.

klasse Person {
  konstruktør (navn) {
    this.name = navn
  }
  Hej() {
    returner 'Hej, jeg er' + this.name + '.'
  }
}

En klasse har en identifikator, som vi kan bruge til at oprette nye objekter ved hjælp af ny ClassIdentifier ().

Når objektet initialiseres, kaldes konstruktormetoden, hvor eventuelle parametre er passeret.

En klasse har også så mange metoder, som den har brug for. I dette tilfælde er hej en metode og kan kaldes på alle objekter, der stammer fra denne klasse:

const flavio = ny person ('Flavio')
flavio.hello ()

Klasse arv

En klasse kan udvide en anden klasse, og objekter, der er initialiseret ved hjælp af denne klasse, arver alle metoder for begge klasser.

Hvis den arvede klasse har en metode med samme navn som en af ​​de klasser, der er højere i hierarkiet, har den nærmeste metode forrang:

klasse Programmer udvider Person {
  Hej() {
    return super.hello () + 'Jeg er en programmør.'
  }
}
const flavio = ny programmør ('Flavio')
flavio.hello ()

(ovenstående program udskriver "Hej, jeg er Flavio. Jeg er en programmør.")

Klasser har ikke eksplicitte klassevariabeldeklarationer, men du skal initialisere enhver variabel i konstruktøren.

Inde i en klasse kan du henvise til den overordnede klasse, der kalder super ().

Statiske metoder

Normalt defineres metoder på forekomsten, ikke i klassen.

Statiske metoder udføres i stedet for klassen:

klasse Person {
  statisk generiskHello () {
    vende tilbage 'Hej'
  }
}
Person.genericHello () // Hej

Private metoder

JavaScript har ikke en indbygget måde at definere private eller beskyttede metoder.

Der er løsninger, men jeg vil ikke beskrive dem her.

Getters og seters

Du kan tilføje metoder, der er præfixeret med get eller indstillet til at oprette en getter og setter, som er to forskellige kodestykker, der udføres baseret på, hvad du laver: adgang til variablen eller ændring af dens værdi.

klasse Person {
  konstruktør (navn) {
    dette.navn = navn
  }
  sæt navn (værdi) {
    dette.navn = værdi
  }
  få navn () {
    returner dette._navn
  }
}

Hvis du kun har en getter, kan ejendommen ikke indstilles, og ethvert forsøg på at gøre det ignoreres:

klasse Person {
  konstruktør (navn) {
    dette.navn = navn
  }
  få navn () {
    returner dette._navn
  }
}

Hvis du kun har en setter, kan du ændre værdien, men ikke få adgang til den udefra:

klasse Person {
  konstruktør (navn) {
    dette.navn = navn
  }
  sæt navn (værdi) {
    dette.navn = værdi
  }
}

Standardparametre

Dette er en doSomething-funktion, der accepterer param1.

const doSomething = (param1) => {
}

Vi kan tilføje en standardværdi for param1, hvis funktionen aktiveres uden at specificere en parameter:

const doSomething = (param1 = 'test') => {
}

Dette fungerer naturligvis også til flere parametre:

const doSomething = (param1 = 'test', param2 = 'test2') => {
}

Hvad hvis du har et unikt objekt med parameterværdier i det?

Hvis vi engang var nødt til at videregive et objekt med indstillinger til en funktion for at have standardværdier for disse indstillinger, hvis en af ​​dem ikke var defineret, skulle du tilføje en lille smule kode inde i funktionen:

const colorize = (indstillinger) => {
  hvis (! indstillinger) {
    indstillinger = {}
  }
  const color = ('farve' i indstillinger)? option.color: 'gul'
  ...
}

Med destruktion kan du levere standardværdier, hvilket forenkler koden meget:

const colorize = ({color = 'gul'}) => {
  ...
}

Hvis der ikke sendes noget objekt, når vi kalder vores farvelægningsfunktion, kan vi på lignende måde tildele et tomt objekt som standard:

const spin = ({color = 'gul'} = {}) => {
  ...
}

Skabelon bogstaver

Skabelonlitterære giver dig mulighed for at arbejde med strenge på en ny måde sammenlignet med ES5 og nedenfor.

Syntaxen er ved første øjekast meget enkel, brug bare backticks i stedet for enkelt eller dobbelt citater:

const a_string = `noget '

De er unikke, fordi de leverer en masse funktioner, som normale strenge bygget med citater ikke, især:

  • de tilbyder en stor syntaks til at definere multiline strenge
  • de giver en nem måde at interpolere variabler og udtryk i strenge
  • de giver dig mulighed for at oprette DSL'er med skabelon tags (DSL betyder domænespecifikt sprog, og det er f.eks. brugt i React by Styled Components, til at definere CSS for en komponent)

Lad os dykke ned i hver af disse detaljerede.

Multiline strenge

Forud for ES6, for at oprette en streng, der spænder over to linjer, måtte du bruge tegnet \ i slutningen af ​​en linje:

const streng =
  'første del \
anden del '

Dette gør det muligt at oprette en streng på 2 linjer, men den gengives på kun en linje:

første del anden del

For at gengive strengen også på flere linjer skal du eksplicit tilføje \ n i slutningen af ​​hver linje som denne:

const streng =
  'første linje \ n \
anden linje '

eller

const string = 'første linje \ n' + 'anden linje'

Skabelonlitterære gør multiline strenge meget enklere.

Når en skabelon bogstavelig er åbnet med backtick, skal du bare trykke på enter for at oprette en ny linje uden specialtegn, og den er gengivet som den er:

const string = `Hej
dette
snor
er fantastisk! `

Husk, at rummet er meningsfuldt, så gør dette:

const string = `Først
                Second`

vil oprette en streng som denne:

Først
                Anden

en nem måde at løse dette problem er ved at have en tom første linje og tilføje metoden Trim () lige efter den lukkende backtick, hvilket vil fjerne enhver plads før den første karakter:

const string = `
Først
Second`.trim ()

Interpolation

Skabelonlitterære giver en nem måde at interpolere variabler og udtryk i strenge.

Det gør du ved at bruge syntaks $ {...}:

const var = 'test'
const string = `noget $ {var}` // noget test

inden for $ {} kan du tilføje alt, selv udtryk:

const string = `noget $ {1 + 2 + 3}`
const string2 = `noget $ {foo ()? 'x': 'y'} `

Skabelon tags

Mærkede skabeloner er en funktion, der muligvis lyder mindre nyttigt til at begynde med for dig, men den bruges faktisk af mange populære biblioteker omkring, som Styled Components eller Apollo, GraphQL-klienten / server-lib, så det er vigtigt at forstå, hvordan det fungerer.

I stylede komponenter bruges skabelonmærker til at definere CSS-strenge:

const-knap = stylet.knap`
  skriftstørrelse: 1,5 em;
  baggrundsfarve: sort;
  farve: hvid;
`

I Apollo-skabelon bruges tags til at definere et GraphQL-forespørgselskema:

const query = gql`
  forespørgsel {
    ...
  }
`

Template-tagsne styled.button og gql, der er fremhævet i disse eksempler, er bare funktioner:

funktion gql (bogstavelige, ... udtryk) {}

denne funktion returnerer en streng, der kan være resultatet af enhver form for beregning.

literals er en matrix indeholdende skabelonets bogstavelige indhold, der er symboliseret ved udtryk-interpolationer.

udtryk indeholder alle interpolationer.

Hvis vi tager et eksempel ovenfor:

const string = `noget $ {1 + 2 + 3}`

literals er en matrix med to genstande. Den første er noget, strengen indtil den første interpolation, og den anden er en tom streng, mellemrummet mellem slutningen af ​​den første interpolation (vi har kun en) og slutningen af ​​strengen.

udtryk i dette tilfælde er en matrix med et enkelt punkt, 6.

Et mere komplekst eksempel er:

const string = `noget
en anden $ {'x'}
ny linje $ {1 + 2 + 3}
test`

i dette tilfælde er bogstaver en matrix, hvor den første vare er:

; `Noget
en anden `

det andet er:

; `
ny linje `

og den tredje er:

; `
test`

udtryk i dette tilfælde er en matrix med to poster, x og 6.

Den funktion, der overføres disse værdier, kan gøre hvad som helst med dem, og dette er kraften i denne slags funktion.

Det mest enkle eksempel er at gentage, hvad strenginterpolationen gør ved at forbinde bogstaver og udtryk:

const interpolated = interpolate`Jeg betalte $ 10,00 €

og sådan fungerer interpolat:

funktion interpolere (bogstavelige, ... udtryk) {
  lad streng = ``
  for (konst [i, val] af udtryk) {
    streng + = bogstaver [i] + val
  }
  streng + = literals [literals.length - 1]
  returstreng
}

Destruktureringsopgaver

Givet et objekt, kan du udtrække bare nogle værdier og sætte dem i navngivne variabler:

const person = {
  fornavn: 'Tom',
  efternavn: 'Krydstogt',
  skuespiller: sandt,
  alder: 54, // sammensat
}
const {firstName: name, age} = person

navn og alder indeholder de ønskede værdier.

Syntaks fungerer også på matriser:

const a = [1,2,3,4,5]
const [første, anden] = a

Denne erklæring opretter 3 nye variabler ved at hente elementerne med indeks 0, 1, 4 fra matrixen a:

const [første, anden,, femte] = a

Forbedrede objektlitterære

I ES2015 fik Object Literals supermagter.

Forenklet syntaks til at inkludere variabler

I stedet for at gøre

const noget = 'y'
const x = {
  noget: noget
}

du kan gøre

const noget = 'y'
const x = {
  noget
}

Prototype

En prototype kan specificeres med

const anObject = {y: 'y'}
const x = {
  __proto__: anObject
}

super()

const anObject = {y: 'y', test: () => 'zoo'}
const x = {
  __proto__: anObject,
  test () {
    return super.test () + 'x'
  }
}
x.test () // zoox

Dynamiske egenskaber

const x = {
  ['a' + '_' + 'b']: 'z'
}
x.a_b // z

For-of-loop

ES5 tilbage i 2009 introducerede for hver () loops. Mens de var rart, tilbød de ingen måde at bryde, som for løkker altid gjorde.

ES2015 introducerede for-of-loopen, der kombinerer kortfattetheden af ​​forEach med evnen til at bryde:

// iterere over værdien
for (const v for ['a', 'b', 'c']) {
  console.log (v);
}
// få også indekset ved hjælp af 'poster ()'
for (konst [i, v] af ['a', 'b', 'c']. poster ()) {
  console.log (index) // index
  console.log (værdi) // værdi
}

Bemærk brugen af ​​const. Denne løkke skaber et nyt omfang i enhver iteration, så vi kan bruge det sikkert i stedet for at lade.

Forskellen med for ... i er:

  • for ... af iterater over egenskabsværdierne
  • for ... i iterates egenskabsnavne

Promises

Et løfte defineres almindeligvis som en proxy for en værdi, der til sidst vil blive tilgængelig.

Løfter er en måde at håndtere asynkron kode på uden at skrive for mange tilbagekald i din kode.

Async-funktioner bruger API-løfterne som deres byggesten, så forståelse af dem er grundlæggende, selvom du i nyere kode sandsynligvis vil bruge async-funktioner i stedet for løfter.

Hvordan løfter fungerer, kort fortalt

Når et løfte er blevet kaldt, vil det starte i afventende tilstand. Dette betyder, at opkaldsfunktionen fortsætter udførelsen, mens den venter på løftet om at udføre sin egen behandling og give opkaldsfunktionen en vis feedback.

På dette tidspunkt venter opkaldsfunktionen på, at den enten returnerer løftet i en løst tilstand eller i en afvist tilstand, men som du ved, JavaScript er asynkron, så fortsætter funktionen sin udførelse, mens løftet fungerer.

Hvilke løfter om brug af JS API bruger?

Ud over din egen kode og bibliotekskode bruges løfter ved standard moderne Web API'er som:

  • Battery API
  • Fetch API
  • Servicemedarbejdere

Det er usandsynligt, at du i moderne JavaScript ikke finder dig selv ved at bruge løfter, så lad os begynde at dykke lige ind i dem.

Oprettelse af et løfte

Promise API afslører en Promise-konstruktør, som du initialiserer ved hjælp af nye Promise ():

lad gjort = sandt
const isItDoneYet = nyt løfte ((løse, afvis) => {
  hvis (gjort) {
    const workDone = 'Her er den ting, jeg har bygget'
    løse (workDone)
  } andet {
    const why = 'Arbejder stadig med noget andet'
    afvise (hvorfor)
  }
})

Som du kan se løftet kontrollerer den udførte globale konstant, og hvis det er sandt, returnerer vi et løst løfte, ellers et afvist løfte.

Ved hjælp af løsning og afvisning kan vi kommunikere en værdi tilbage, i ovenstående tilfælde returnerer vi bare en streng, men det kan også være et objekt.

Forbruger et løfte

I det sidste afsnit introducerede vi, hvordan et løfte oprettes.

Lad os nu se, hvordan løftet kan forbruges eller bruges.

const isItDoneYet = nyt løfte ()
// ...
const checkIfItsDone = () => {
  isItDoneYet
    .then (ok => {
      console.log (ok)
    })
    .fangst (err => {
      console.error (err)
    })
}

Kørsel checkIfItsDone () udfører løftet isItDoneYet () og vil vente på, at det løses ved hjælp af den derefter tilbagekald, og hvis der er en fejl, vil den håndtere det i fangstopkaldet.

Kæde løfter

Et løfte kan returneres til et andet løfte ved at skabe en kæde af løfter.

Et godt eksempel på kædeløfter er givet af Fetch API, et lag oven på XMLHttpRequest API, som vi kan bruge til at få en ressource og stå i kø for en kæde med løfter til at udføre, når ressourcen hentes.

Fetch API er en løftebaseret mekanisme, og at ringe hente () svarer til at definere vores eget løfte ved hjælp af nyt løfte ().

Eksempel på kædeløfter

const status = respons => {
  if (response.status> = 200 && response.status <300) {
    returner Promise.resolve (svar)
  }
  returner Promise.reject (ny fejl (respons.statusText))
}
const json = respons => respons.json ()
hente ( '/ todos.json')
  Ringing (status)
  Ringing (JSON)
  .then (data => {
    console.log ('Forespørgsel lykkedes med JSON-svar', data)
  })
  .fang (fejl => {
    console.log ('Anmodning mislykkedes', fejl)
  })

I dette eksempel kalder vi hent () for at få en liste over TODO-emner fra filen todos.json, der findes i domæneroden, og vi opretter en kæde af løfter.

At køre hente () returnerer et svar, der har mange egenskaber, og inden for dem, vi refererer til:

  • status, en numerisk værdi, der repræsenterer HTTP-statuskoden
  • statusTekst, en statusmeddelelse, som er OK, hvis anmodningen lykkedes

respons har også en json () -metode, der returnerer et løfte, der løses med indholdet af kroppen behandlet og omdannet til JSON.

Så i betragtning af disse lokaler er det, hvad der sker: det første løfte i kæden er en funktion, som vi definerede, kaldet status (), der kontrollerer responsstatusen, og hvis det ikke er en succesrespons (mellem 200 og 299), afviser den løfte.

Denne handling vil forårsage, at løftekæden springer over alle de anførte kædede løfter, og springer direkte til fangst () -sætningen i bunden og logger den anmodede mislykkede tekst sammen med fejlmeddelelsen.

Hvis det i stedet lykkes, kalder det json () -funktionen, vi definerede. Da det forrige løfte, når det lykkedes, returnerede svarobjektet, får vi det som et input til det andet løfte.

I dette tilfælde returnerer vi de data, som JSON behandles, så det tredje løfte modtager JSON direkte:

.then ((data) => {
  console.log ('Forespørgsel lykkedes med JSON-svar', data)
})

og vi logger det på konsollen.

Fejl ved håndtering

I ovenstående eksempel i det foregående afsnit havde vi en fangst, der blev knyttet til løftekæden.

Når noget i løftekæden mislykkes og rejser en fejl eller afviser løftet, går kontrollen til den nærmeste fangst () erklæring nede i kæden.

nye løfte ((løse, afvis) => {
  smid ny fejl ('Fejl')
}). fangst (err => {
  console.error (err)
})
// eller
nye løfte ((løse, afvis) => {
  afvise ( 'Fejl')
}). fangst (err => {
  console.error (err)
})

Faldende fejl

Hvis der inden for fangsten () opstår en fejl, kan du tilføje en anden fangst () for at håndtere den osv.

nye løfte ((løse, afvis) => {
  smid ny fejl ('Fejl')
})
  .fangst (err => {
    smid ny fejl ('Fejl')
  })
  .fangst (err => {
    console.error (err)
  })

Orkestrerende løfter

Promise.all ()

Hvis du skal synkronisere forskellige løfter, hjælper Promise.all () dig med at definere en liste med løfter og udføre noget, når de alle er løst.

Eksempel:

const f1 = hente ('/ noget.json')
const f2 = hente ('/ iets2.json')
Promise.all ([f1, f2])
  .then (res => {
    console.log ('Array of results', res)
  })
  .fangst (err => {
    console.error (err)
  })

Syntaks til destruktion af tildelingen af ​​ES2015 giver dig også mulighed for at gøre det

Promise.all ([f1, f2]). Derefter (([res1, res2]) => {
  console.log ('Resultater', res1, res2)
})

Du er ikke begrænset til at bruge hente selvfølgelig, ethvert løfte er godt at gå.

Promise.race ()

Promise.race () kører, så snart et af de løfter, du giver det, løses, og det kører den vedhæftede tilbagekald bare én gang med resultatet af det første løfte, der er løst.

Eksempel:

const loverOne = new Promise ((løse, afvis) => {
  setTimeout (resolut, 500, 'en')
})
const loverTwo = new Promise ((løse, afvis) => {
  setTimeout (resolut, 100, 'to')
})
Promise.race ([loverOne, loverTwo]). Derefter (resultat => {
  console.log (resultat) // 'to'
})

moduler

ES-moduler er ECMAScript-standarden til arbejde med moduler.

Mens Node.js har brugt CommonJS-standarden i årevis, havde browseren aldrig et modulsystem, da enhver større beslutning, f.eks. Et modulsystem, skal først standardiseres med ECMAScript og derefter implementeres af browseren.

Denne standardiseringsproces afsluttet med ES2015 og browsere begyndte at implementere denne standard og forsøgte at holde alt godt på linje, fungerer alt på samme måde, og nu understøttes ES-moduler i Chrome, Safari, Edge og Firefox (siden version 60).

Moduler er meget seje, fordi de giver dig mulighed for at indkapsle alle mulige funktioner og udsætte denne funktionalitet for andre JavaScript-filer som biblioteker.

ES-modulers syntaks

Syntaks for at importere et modul er:

importpakke fra 'modulnavn'

mens CommonJS bruger

const package = kræve ('modul-navn')

Et modul er en JavaScript-fil, der eksporterer en eller flere værdier (objekter, funktioner eller variabler) ved hjælp af eksportnøgleordet. For eksempel eksporterer dette modul en funktion, der returnerer en streng med store bogstaver:

uppercase.js
eksport standard str => str.toUpperCase ()

I dette eksempel definerer modulet en enkelt standardeksport, så det kan være en anonym funktion. Ellers behøver det et navn for at skelne det fra anden eksport.

Nu kan ethvert andet JavaScript-modul importere funktionaliteten, der tilbydes af uppercase.js ved at importere det.

En HTML-side kan tilføje et modul ved hjælp af et

Bemærk: Denne modulimport opfører sig som en udskudning af scriptbelastning. Se effektivt indlæse JavaScript med udsættelse og async

Det er vigtigt at bemærke, at ethvert script indlæst med type = "modul" indlæses i streng tilstand.

I dette eksempel definerer modulet med store bogstaver.js en standardeksport, så når vi importerer det, kan vi tildele det et navn, vi foretrækker:

import tilUpperCase fra './uppercase.js'

og vi kan bruge det:

toUpperCase ('test') // 'TEST'

Du kan også bruge en absolut sti til modulimport til at referere moduler defineret på et andet domæne:

import tilUpperCase fra 'https://flavio-es-modules-example.glitch.me/uppercase.js'

Dette er også gyldig import-syntaks:

import {toUpperCase} fra '/uppercase.js'
import {toUpperCase} fra '../uppercase.js'

Dette er ikke:

import {toUpperCase} fra 'uppercase.js'
import {toUpperCase} fra 'utils / uppercase.js'

Det er enten absolut, eller har et ./ eller / før navnet.

Andre import / eksportmuligheder

Vi så dette eksempel ovenfor:

eksport standard str => str.toUpperCase ()

Dette opretter en standardeksport. I en fil kan du imidlertid eksportere mere end én ting ved at bruge denne syntaks:

const a = 1
const b = 2
const c = 3
eksport {a, b, c}

Et andet modul kan importere al denne eksport vha

import * fra 'modul'

Du kan importere kun et par af disse eksport ved hjælp af den destruktive opgave:

import {a} fra 'modul'
import {a, b} fra 'modul'

Du kan omdøbe enhver import for nemheds skyld ved hjælp af som:

import {a, b som to} fra 'modul'

Du kan importere standardeksporten og enhver ikke-standardeksport ved navn, som i denne almindelige React-import:

import React, {Component} fra 'react'

Du kan se et ES-modulereksempel her: https://glitch.com/edit/#!/flavio-es-modules-example?path=index.html

CORS

Moduler hentes vha. CORS. Dette betyder, at hvis du refererer til scripts fra andre domæner, skal de have et gyldigt CORS-header, der tillader indlæsning på tværs af websteder (som Access-Control-Allow-Origin: *)

Hvad med browsere, der ikke understøtter moduler?

Brug en kombination af type = "modul" og nomodule:

Indpakning af moduler

ES-moduler er en af ​​de største funktioner introduceret i moderne browsere. De er en del af ES6, men vejen til at implementere dem har været lang.

Vi kan nu bruge dem! Men vi må også huske, at det at have mere end et par moduler vil få et performance-hit på vores sider, da det er endnu et trin, som browseren skal udføre under kørsel.

Webpack vil sandsynligvis stadig være en enorm spiller, selvom ES-moduler lander i browseren, men at have en sådan funktion direkte indbygget på sproget er enormt for en forening af, hvordan moduler fungerer klientsiden og på Node.js også.

Nye strengmetoder

Enhver strengværdi fik nogle nye instansmetoder:

  • gentage()
  • codePointAt ()

gentage()

Gentager strengene for det specificerede antal gange:

'Ho'.repeat (3) //' HoHoHo '

Returnerer en tom streng, hvis der ikke er nogen parameter, eller parameteren er 0. Hvis parameteren er negativ får du en RangeError.

codePointAt ()

Denne metode kan bruges til at håndtere Unicode-tegn, der ikke kan repræsenteres af en enkelt 16-bit Unicode-enhed, men i stedet har brug for 2.

Ved hjælp af charCodeAt () skal du hente det første og det andet og kombinere dem. Ved hjælp af codePointAt () får du hele tegnet i et opkald.

For eksempel er denne kinesiske karakter “𠮷” sammensat af 2 UTF-16 (Unicode) dele:

"𠮷" .charCodeAt (0) .toString (16) // d842
"𠮷" .charCodeAt (1) .toString (16) // dfb7

Hvis du opretter en ny karakter ved at kombinere disse unicode-tegn:

"\ ud842 \ udfb7" // "𠮷"

Du kan få det samme resultatskilt kodePointAt ():

"𠮷" .codePointAt (0) // 20bb7

Hvis du opretter en ny karakter ved at kombinere disse unicode-tegn:

"\ u {20bb7}" // "𠮷"

Mere om Unicode og arbejde med det i min Unicode-guide.

Nye objektmetoder

ES2015 introducerede flere statiske metoder under navneområdet Objekt:

  • Object.is () bestemmer, om to værdier er den samme værdi
  • Object.assign () bruges til lav kopi af et objekt
  • Object.setPrototypeOf indstiller en objektprototype

Object.is ()

Disse metoder har til formål at hjælpe med at sammenligne værdier.

Anvendelse:

Object.is (a, b)

Resultatet er altid usant, medmindre:

  • a og b er det samme nøjagtige objekt
  • a og b er lige strenge (strengene er ens, når de er sammensat af de samme tegn)
  • a og b er lige store tal (talene er ens, når deres værdi er ens)
  • a og b er begge udefinerede, begge nul, begge NaN, begge sande eller begge falske

0 og -0 er forskellige værdier i JavaScript, så vær opmærksom i dette specielle tilfælde (konverter alle til +0 vha. Operatoren + unary, før du f.eks. Sammenligner).

Object.assign ()

Denne metode introduceres i ES2015, og kopierer alle de mange egne egenskaber for et eller flere objekter til et andet.

Dets primære brugssag er at oprette en lav kopi af et objekt.

const copied = Object.assign ({}, original)

Som en lav kopi klones værdier og objektreferencer kopieres (ikke selve objekterne), så hvis du redigerer en objektegenskab i det originale objekt, ændres den også i det kopierede objekt, da det refererede indre objekt er det samme:

const original = {
  navn: 'Fiesta',
  bil: {
    farve: 'blå'
  }
}
const copied = Object.assign ({}, original)
original.name = 'Fokus'
original.car.color = 'gul'
copied.name // Fiesta
kopieret.car.color // gul

Jeg nævnte "en eller flere":

const wisePerson = {
  isWise: sandt
}
const foolishPerson = {
  isFoolish: sandt
}
const wiseAndFoolishPerson = Object.assign ({}, wisePerson, foolishPerson)
console.log (wiseAndFoolishPerson) // {isWise: true, isFoolish: true}

Object.setPrototypeOf ()

Indstil prototypen til et objekt. Accepterer to argumenter: objektet og prototypen.

Anvendelse:

Object.setPrototypeOf (objekt, prototype)

Eksempel:

const animal = {
  isAnimal: sandt
}
const pattedyr = {
  isMammal: sandt
}
pattedyr .__ proto__ = dyr
pattedyr.isDyr // sandt
const dog = Object.create (dyr)
dog.isDyr // sandt
console.log (dog.isMammal) // undefined
Object.setPrototypeOf (hund, pattedyr)
dog.isDyr // sandt
dog.isMammal // sandt

Spredningsoperatøren

Du kan udvide en matrix, et objekt eller en streng ved hjælp af spredningsoperatøren ...

Lad os starte med et arrayeksempel. Givet

const a = [1, 2, 3]

Du kan oprette en ny matrix vha

const b = [... a, 4, 5, 6]

Du kan også oprette en kopi af en matrix vha

const c = [... a]

Dette fungerer også til genstande. Klon et objekt med:

const newObj = {... oldObj}

Ved hjælp af strenge opretter spredningsoperatøren en matrix med hver char i strengen:

const hey = 'hey'
const arrayized = [... hey] // ['h', 'e', ​​'y']

Denne operatør har nogle temmelig nyttige applikationer. Den vigtigste er evnen til at bruge en matrix som funktionsargument på en meget enkel måde:

const f = (foo, bar) => {}
const a = [1, 2]
f (... a)

(I fortiden kunne du gøre dette ved at bruge f.apply (null, a), men det er ikke så rart og læseligt.)

Det resterende element er nyttigt, når man arbejder med array-destruktion:

const numre = [1, 2, 3, 4, 5]
[første, anden, ... andre] = tal

og spredte elementer:

const numre = [1, 2, 3, 4, 5]
const sum = (a, b, c, d, e) => a + b + c + d + e
const sum = sum (... tal)

ES2018 introducerer hvileegenskaber, som er de samme, men for objekter.

Hvilegenskaber:

const {first, second, ... others} = {
  første: 1,
  anden: 2,
  tredje: 3,
  fjerde: 4,
  femte: 5
}
første // 1
anden // 2
andre // {tredje: 3, fjerde: 4, femte: 5}

Spredte egenskaber tillader os at oprette et nyt objekt ved at kombinere egenskaberne for det objekt, der er sendt efter spredningsoperatøren:

const items = {first, second, ... others}
varer // {første: 1, anden: 2, tredje: 3, fjerde: 4, femte: 5}

Sæt

En sæt datastruktur giver os mulighed for at tilføje data til en container.

Et sæt er en samling objekter eller primitive typer (strenge, tal eller booleanere), og du kan tænke på det som et kort, hvor værdier bruges som kortnøgler, hvor kortværdien altid er en boolsk sand.

Initialiser et sæt

Et sæt initialiseres ved at ringe til:

const s = nyt sæt ()

Føj emner til et sæt

Du kan tilføje elementer til sættet ved hjælp af tilføjelsesmetoden:

s.add ( 'en')
s.add ( 'to')

Et sæt gemmer kun unikke elementer, så at kalde s.add ('en') flere gange ikke tilføjer nye elementer.

Du kan ikke tilføje flere elementer til et sæt på samme tid. Du skal kalde tilføj () flere gange.

Kontroller, om en vare er i sættet

Når et element er i sættet, kan vi kontrollere, om sættet indeholder det:

s.has ('en') // sandt
s.has ('tre') // falsk

Slet et element fra en indstillet tast

Brug metoden slet ():

s.delete ( 'en')

Bestem antallet af elementer i et sæt

Brug egenskaben størrelse:

s.size

Slet alle elementer fra et sæt

Brug metoden clear ():

s.clear ()

Iterér elementerne i et sæt

Brug tasterne () eller værdier () -metoderne - de er ækvivalente:

for (konst k af s.keys ()) {
  console.log (k)
}
for (const k of s.values ​​()) {
  console.log (k)
}

Metoden entry () returnerer en iterator, som du kan bruge sådan:

const i = s.entries ()
console.log (i.next ())

ved at kalde i.next () returneres hvert element som et {value, done = false} objekt, indtil iteratoren slutter, hvor det gjort er sandt.

Du kan også bruge forEach () -metoden i sættet:

s.forEach (v => console.log (v))

eller du kan bare bruge sættet i en for..of loop:

for (konst k af s) {
  console.log (k)
}

Initialiser et sæt med værdier

Du kan initialisere et sæt med et sæt værdier:

const s = nyt sæt ([1, 2, 3, 4])

Konverter sæt-tasterne til en matrix

const a = [... s.keys ()]
// eller
const a = [... s.values ​​()]

Et svagt sæt

Et WeakSet er en speciel slags sæt.

I et sæt indsamles genstande aldrig. Et WeakSet lader i stedet alle dets genstande samles frit. Hver nøgle i et WeakSet er et objekt. Når henvisningen til dette objekt mistes, kan værdien indsamles skrald.

Her er de vigtigste forskelle:

  1. du kan ikke iterere over WeakSet
  2. du kan ikke rydde alle emner fra et WeakSet
  3. du kan ikke kontrollere dens størrelse

Et WeakSet bruges generelt af ramme-niveau-kode og afslører kun disse metoder:

  • tilføje()
  • har ()
  • slette ()

Kort

En kortdatastruktur giver os mulighed for at knytte data til en nøgle.

Før ES6

Før introduktionen brugte folk generelt objekter som kort ved at knytte et objekt eller værdi til en bestemt nøgleværdi:

const car = {}
bil ['color'] = 'rød'
car.owner = 'Flavio'
console.log (bil ['farve']) // rød
console.log (car.color) // rød
console.log (car.owner) // Flavio
console.log (bil ['ejer']) // Flavio

Indtast kort

ES6 introducerede Map-datastrukturen, hvilket giver os et ordentligt værktøj til at håndtere denne slags dataorganisation.

Et kort initialiseres ved at ringe til:

const m = nyt kort ()

Føj genstande til et kort

Du kan tilføje elementer til kortet ved hjælp af den indstillede metode:

m.set ('farve', 'rød')
m.set ('alder', 2)

Hent en vare fra et kort efter nøgle

Og du kan få elementer ud af et kort ved at bruge get:

const color = m.get ('farve')
const age = m.get ('age')

Slet et element fra et kort efter nøgle

Brug metoden slet ():

m.delete ( 'farve')

Slet alle elementer fra et kort

Brug metoden clear ():

m.clear ()

Kontroller, om et kort indeholder en vare efter nøgle

Brug metoden ():

const hasColor = m.has ('farve')

Find antallet af varer på et kort

Brug egenskaben størrelse:

const størrelse = m. størrelse

Initialiser et kort med værdier

Du kan initialisere et kort med et sæt værdier:

const m = nyt kort ([['farve', 'rødt'], ['ejer', 'Flavio'], ['alder', 2]])

Kortnøgler

Ligesom enhver værdi (objekt, array, streng, nummer) kan bruges som værdien af ​​nøgleværdieindgangen til et kortemne, kan enhver værdi bruges som nøglen, endda objekter.

Hvis du forsøger at få en ikke-eksisterende nøgle ved hjælp af get () ud af et kort, returnerer den ubegrænset.

Mærkelige situationer, som du næsten aldrig finder i det virkelige liv

const m = nyt kort ()
m.set (NaN, 'test')
m.get (NaN) // test
const m = nyt kort ()
m.set (+0, 'test')
m.get (-0) // test

Iterere over korttaster

Kort tilbyder metoden nøgler (), som vi kan bruge til at itereere på alle tasterne:

for (const k of m.keys ()) {
  console.log (k)
}

Iterere over kortværdier

Kortobjektet tilbyder metoderne værdier (), som vi kan bruge til at itereere på alle værdier:

for (const v of m.values ​​()) {
  console.log (v)
}

Iterere over kortnøgle, værdipar

Kortobjektet tilbyder metoden entry (), som vi kan bruge til at iterere på alle værdier:

for (konst [k, v] af m.entries ()) {
  console.log (k, v)
}

som kan forenkles til

for (const [k, v] af m) {
  console.log (k, v)
}

Konverter korttasterne til en matrix

const a = [... m.keys ()]

Konverter kortværdierne til en matrix

const a = [... m.værdier ()]

WeakMap

En WeakMap er en speciel type kort.

I et kortobjekt indsamles genstande aldrig. En WeakMap lader i stedet alle dets genstande samles frit. Hver nøgle på et WeakMap er et objekt. Når henvisningen til dette objekt mistes, kan værdien indsamles skrald.

Her er de vigtigste forskelle:

  1. du kan ikke iterere over tasterne eller værdierne (eller nøgleværdier) på en WeakMap
  2. du kan ikke rydde alle elementer fra en WeakMap
  3. du kan ikke kontrollere dens størrelse

En WeakMap udsætter disse metoder, der svarer til kortmetoderne:

  • få (k)
  • sæt (k, v)
  • har (k)
  • slette (k)

Brugssagerne af en WeakMap er mindre tydelige end dem på et kort, og du kan måske aldrig finde behovet for dem, men det kan i det væsentlige bruges til at oprette en hukommelsesfølsom cache, der ikke vil forstyrre indsamlingen af ​​skrald, eller til omhyggelig indkapsling og skjult information.

generatorer

Generatorer er en speciel form for funktion med evnen til at stoppe sig selv og genoptage senere, så andre kode kan køre i mellemtiden.

Se den fulde JavaScript-generatorguide for en detaljeret forklaring af emnet.

Koden bestemmer, at den skal vente, så den lader anden kode "i køen" køre og holder retten til at genoptage sine operationer, "når den ting, den venter på," er gjort.

Alt dette gøres med et enkelt, simpelt nøgleord: udbytte. Når en generator indeholder dette nøgleord, stoppes udførelsen.

En generator kan indeholde mange udbyttesøgeord, og dermed stoppe sig flere gange, og det identificeres ved hjælp af * -funktionsnøgleordet, som ikke skal forveksles med den markørderferenceoperator, der bruges i programmeringssprog på lavere niveau, f.eks. C, C ++ eller Go.

Generatorer aktiverer helt nye paradigmer af programmering i JavaScript, hvilket tillader:

  • 2-vejs kommunikation, mens en generator kører
  • lang levetid, mens sløjfer, der ikke fryser dit program

Her er et eksempel på en generator, der forklarer, hvordan det hele fungerer.

funktion * lommeregner (input) {
    var doubleThat = 2 * (udbytte (input / 2))
    var en anden = udbytte (doubleThat)
    return (input * doubleThat * another)
}

Vi initialiserer det med

const calc = lommeregner (10)

Så starter vi iteratoren på vores generator:

calc.next ()

Denne første iteration starter iteratoren. Koden returnerer dette objekt:

{
  udført: falsk
  værdi: 5
}

Hvad der sker er: koden kører funktionen med input = 10, da den blev sendt i generatorkonstruktoren. Det kører, indtil det når udbyttet, og returnerer indholdet af udbyttet: input / 2 = 5. Så vi fik en værdi på 5, og indikationen på, at iterationen ikke er udført (funktionen er bare sat på pause).

I den anden iteration passerer vi værdien 7:

calc.next (7)

og hvad vi fik tilbage er:

{
  udført: falsk
  værdi: 14
}

7 blev placeret som værdien af ​​doubleThat. Vigtigt: du har måske læst som input / 2 var argumentet, men det er bare returværdien af ​​den første iteration. Vi springer det nu over og bruger den nye inputværdi 7 og multiplicerer den med 2.

Vi når derefter det andet udbytte, og det returnerer dobbeltDet, så den returnerede værdi er 14.

I den næste og sidste iteration passerer vi 100

calc.next (100)

og til gengæld fik vi

{
  gjort: sandt
  værdi: 14000
}

Efterhånden som iterationen er færdig (der findes ikke flere udbytteord), og vi vender bare tilbage (input * dobbeltDet * andet), som udgør 10 * 14 * 100.

Disse var de funktioner, der blev introduceret i ES2015. Lad os nu dykke ned i ES2016, som er meget mindre i omfang.

Array.prototype.includes ()

Denne funktion introducerer en mere læsbar syntaks til kontrol af, om en matrix indeholder et element.

Med ES6 og lavere, for at kontrollere, om en matrix indeholdt et element, var du nødt til at bruge indexOf, som kontrollerer indekset i matrixen, og returnerer -1, hvis elementet ikke er der.

Da -1 vurderes som en ægte værdi, kunne du f.eks. Ikke gøre det

if (! [1,2] .indexOf (3)) {
  console.log ('Ikke fundet')
}

Med denne funktion introduceret i ES7 kan vi gøre

hvis (! [1,2]. inkluderer (3)) {
  console.log ('Ikke fundet')
}

Eksponentiering Operator

Eksponentieringsoperatøren ** svarer til Math.pow (), men bragt ind på sproget i stedet for at være en biblioteksfunktion.

Math.pow (4, 2) == 4 ** 2

Denne funktion er en dejlig tilføjelse til matematisk intensive JS-applikationer.

** Operatøren er standardiseret på tværs af mange sprog, herunder Python, Ruby, MATLAB, Lua, Perl og mange andre.

Det var de funktioner, der blev introduceret i 2016. Lad os nu dykke ind i 2017

Streng polstring

Formålet med strengpolstring er at føje tegn til en streng, så den når en bestemt længde.

ES2017 introducerer to strengmetoder: padStart () og padEnd ().

padStart (targetLength [, padString])
padEnd (targetLength [, padString])

Prøvebrug:

Object.values ​​()

Denne metode returnerer en matrix, der indeholder alle objektets egenskabsværdier.

Anvendelse:

const person = {navn: 'Fred', alder: 87}
Object.values ​​(person) // ['Fred', 87]

Object.values ​​() fungerer også med arrays:

const people = ['Fred', 'Tony']
Object.values ​​(people) // ['Fred', 'Tony']

Object.entries ()

Denne metode returnerer en matrix, der indeholder alle objektets egne egenskaber, som en matrix med [nøgle, værdi] -par.

Anvendelse:

const person = {navn: 'Fred', alder: 87}
Object.entries (person) // [['navn', 'Fred'], ['alder', 87]]

Object.entries () fungerer også med matriser:

const people = ['Fred', 'Tony']
Object.entries (mennesker) // [['0', 'Fred'], ['1', 'Tony']]

Object.getOwnPropertyDescriptors ()

Denne metode returnerer alle egne (ikke-arvede) egenskabsbeskrivelser af et objekt.

Ethvert objekt i JavaScript har et sæt egenskaber, og hver af disse egenskaber har en deskriptor.

En deskriptor er et sæt attributter for en egenskab, og den er sammensat af en undergruppe af følgende:

  • værdi: ejendommens værdi
  • skrivbar: sandt ejendommen kan ændres
  • get: en getter-funktion for ejendommen, kaldet, når ejendommen læses
  • sæt: en setterfunktion for egenskaben, kaldet når egenskaben er indstillet til en værdi
  • konfigurerbar: hvis falsk, kan ejendommen ikke fjernes eller nogen attribut kan ændres, undtagen dens værdi
  • enumerable: sandt, hvis ejendommen er tællerbar

Object.getOwnPropertyDescriptors (obj) accepterer et objekt og returnerer et objekt med sættet med deskriptorer.

På hvilken måde er dette nyttigt?

ES6 gav os Object.assign (), der kopierer alle utallige egne egenskaber fra et eller flere objekter og returnerer et nyt objekt.

Der er dog et problem med det, fordi det ikke korrekt kopierer egenskaber med ikke-standardattributter.

Hvis et objekt f.eks. Bare har en setter, kopieres det ikke korrekt til et nyt objekt ved hjælp af Object.assign ().

For eksempel med

const person1 = {
    sæt navn (nyt navn) {
        console.log (newname)
    }
}

Dette fungerer ikke:

const person2 = {}
Object.assign (person2, person1)

Men dette fungerer:

const person3 = {}
Object.defineProperties (person3,
  Object.getOwnPropertyDescriptors (person1))

Som du kan se med en simpel konsoltest:

person1.name = 'x'
"x"
person2.name = 'x'
person3.name = 'x'
"x"

person2 savner sætteren, den blev ikke kopieret.

Den samme begrænsning gælder for lavvandede kloning af objekter med Object.create ().

Efterfølgende kommaer

Denne funktion giver mulighed for at have efterfølgende kommaer i funktionserklæringer og i opkald af funktioner:

const doSomething = (var1, var2,) => {
  // ...
}
doSomething ('test2', 'test2',)

Denne ændring vil tilskynde udviklere til at stoppe den grimme "komma ved starten af ​​linjen" -vanen.

Async-funktioner

JavaScript udviklede sig på meget kort tid fra tilbagekald til løfter (ES2015), og siden ES2017 asynkron JavaScript er endnu enklere med syntaks async / venter.

Async-funktioner er en kombination af løfter og generatorer, og dybest set er de et højere niveau abstraktion over løfter. Lad mig gentage: async / afventning er bygget på løfter.

Hvorfor blev async / afventning introduceret?

De reducerer kedelpladen omkring løfter, og "begræns ikke kæden" -begrænsningen af ​​kædeløfter.

Da løfter blev introduceret i ES2015, var de beregnet til at løse et problem med asynkron kode, og det gjorde de, men i løbet af de to år, der adskilte ES2015 og ES2017, var det klart, at løfter ikke kunne være den endelige løsning.

Der blev indført løfter for at løse det berømte tilbagekalds-helvede-problem, men de introducerede kompleksitet på egen hånd og syntaks-kompleksitet.

De var gode primitiver, hvor en bedre syntaks kunne udsættes for udviklere, så når tiden var inde fik vi async-funktioner.

De får koden til at se ud som den er synkron, men den er asynkron og ikke-blokerende bag kulisserne.

Hvordan det virker

En async-funktion giver et løfte som i dette eksempel:

const doSomethingAsync = () => {
  returner nyt løfte (resolut => {
    setTimeout (() => resolut ('Jeg gjorde noget'), 3000)
  })
}

Når du ønsker at ringe til denne funktion, venter du uafhængigt, og opkaldskoden stopper, indtil løftet er løst eller afvist. Én advarsel: klientfunktionen skal defineres som async. Her er et eksempel:

const doSomething = async () => {
  console.log (vent på doSomethingAsync ())
}

Et hurtigt eksempel

Dette er et simpelt eksempel på async / afventer, der bruges til at køre en funktion asynkront:

const doSomethingAsync = () => {
  returner nyt løfte (resolut => {
    setTimeout (() => resolut ('Jeg gjorde noget'), 3000)
  })
}
const doSomething = async () => {
  console.log (vent på doSomethingAsync ())
}
console.log ( 'før')
gør noget()
console.log ( 'Efter')

Ovenstående kode udskriver følgende til browserkonsollen:

Før
Efter
Jeg gjorde noget // efter 3'erne

Lov alle tingene

Forberedelse af async-nøgleordet til enhver funktion betyder, at funktionen returnerer et løfte.

Selv hvis det ikke gør det eksplicit, vil det internt give det et løfte tilbage.

Dette er grunden til, at denne kode er gyldig:

const aFunction = async () => {
  return 'test'
}
aFunction (). derefter (alarm) // Dette advarer 'test'

og det er det samme som:

const aFunction = async () => {
  returner Promise.resolve ('test')
}
aFunction (). derefter (alarm) // Dette advarer 'test'

Koden er meget enklere at læse

Som du kan se i eksemplet ovenfor, ser vores kode meget enkel ud. Sammenlign det med kode ved hjælp af almindelige løfter med chaining og callback-funktioner.

Og dette er et meget simpelt eksempel, de største fordele vil opstå, når koden er meget mere kompleks.

For eksempel er det her, hvordan du får en JSON-ressource og analyserer den ved hjælp af løfter:

const getFirstUserData = () => {
  return hent ('/ brugere.json') // hent brugerliste
    .then (response => response.json ()) // analyserer JSON
    .then (brugere => brugere [0]) // vælg den første bruger
    .then (user => hente (`/ brugere / $ {user.name}`)) // hent brugerdata
    .then (userResponse => response.json ()) // analyserer JSON
}
getFirstUserData ()

Og her er den samme funktionalitet, der leveres med venter / async:

const getFirstUserData = async () => {
  const response = vente på hentning ('/ brugere.json') // få brugernes liste
  const-brugere = afventer svar.json () // analyserer JSON
  const user = brugere [0] // vælg den første bruger
  const userResponse = afventer hentning (`/ brugere / $ {user.name}`) // få brugerdata
  const userData = afventer user.json () // analysere JSON
  returner userData
}
getFirstUserData ()

Flere async-funktioner i serie

Async-funktioner kan kædes meget let, og syntaks er meget mere læsbar end med almindelige løfter:

const loverToDoSomething = () => {
  returner nyt løfte (resolut => {
    setTimeout (() => resolut ('Jeg gjorde noget'), 10000)
  })
}
const watchOverSomeoneDoingSomething = async () => {
  const noget = afventer loverToDoSomething ()
  returner noget + 'og jeg så'
}
const watchOverSomeoneWatchingSomeoneDoingSomething = async () => {
  const iets = afvente watchOverSomeoneDoingSomething ()
  returner noget + 'og jeg så også'
}
watchOverSomeoneWatchingSomeoneDoingSomething (). derefter (res => {
  console.log (res)
})

Vil udskrive:

Jeg gjorde noget, og jeg så og så også på

Nemmere fejlsøgning

Fejlsøgningsløfter er hårde, fordi debuggeren ikke vil gå over asynkron kode.

Async / venter gør dette meget let, for kompilatoren er det ligesom synkron kode.

Delt hukommelse og atomer

WebWorkers bruges til at oprette multitrådede programmer i browseren.

De tilbyder en meddelelsesprotokol via begivenheder. Siden ES2017 kan du oprette en delt hukommelsesgruppe mellem webarbejdere og deres skaber ved hjælp af en SharedArrayBuffer.

Da det er ukendt, hvor lang tid det tager at udbrede en delet hukommelsesdel for at udbrede sig, er Atomics en måde at håndhæve, at når man læser en værdi, er enhver form for skriveoperation afsluttet.

Mere detaljerede oplysninger herom findes i spec-forslaget, som siden er blevet implementeret.

Dette var ES2017. Lad mig nu introducere ES2018-funktionerne

Hvil / spred egenskaber

ES2015 introducerede begrebet et hvileelement, når man arbejder med array-destruktion:

const numre = [1, 2, 3, 4, 5]
[første, anden, ... andre] = tal

og spredte elementer:

const numre = [1, 2, 3, 4, 5]
const sum = (a, b, c, d, e) => a + b + c + d + e
const sum = sum (... tal)

ES2018 introducerer det samme, men for objekter.

Hvilegenskaber:

const {første, anden, ... andre} = {første: 1, anden: 2, tredje: 3, fjerde: 4, femte: 5}
første // 1
anden // 2
andre // {tredje: 3, fjerde: 4, femte: 5}

Spredningsegenskaber gør det muligt at oprette et nyt objekt ved at kombinere egenskaberne for det objekt, der er sendt efter spredningsoperatøren:

const items = {first, second, ... others}
varer // {første: 1, anden: 2, tredje: 3, fjerde: 4, femte: 5}

Asynkron iteration

Den nye konstruktion, der er ventet på, giver dig mulighed for at bruge et asynkisk iterbart objekt som loop-iteration:

til afventer (const line of readLines (filePath)) {
  console.log (line)
}

Da denne brug venter, kan du kun bruge den inden i async-funktioner, som en normal afventning.

Promise.prototype.finally ()

Når et løfte er opfyldt, kalder det med succes de daværende () metoder, den ene efter den anden.

Hvis noget mislykkes i løbet af dette, springes derefter () -metoderne, og fangsten () -metoden udføres.

endelig () giver dig mulighed for at køre en eller anden kode uanset den vellykkede eller ikke vellykkede gennemførelse af løftet:

hente ( 'file.json')
  .then (data => data.json ())
  .catch (fejl => konsol.error (fejl))
  .finally (() => console.log ('færdig'))

Regelmæssige udtryk forbedringer

ES2018 introducerede en række forbedringer vedrørende regulære udtryk. Jeg anbefaler min tutorial om dem, tilgængelig på https://flaviocopes.com/javascript-regular-expressions/.

Her er de specifikke ES2018-tilføjelser.

RegExp-lookbehind påstande: match en streng afhængigt af, hvad der går forud for den

Dette er en lookahead: du bruger? = Til at matche en streng, der efterfølges af en bestemt substring:

/ Roger (? = Waters) /
/ Roger (? = Waters) /. Test ('Roger is my dog') // falsk
/ Roger (? = Waters) /. Test ('Roger er min hund og Roger Waters er en berømt musiker') // sand

?! udfører den inverse operation, der matcher, hvis en streng ikke følges af en bestemt substring:

/ Roger (?! Waters) /
/ Roger (?! Waters) /. Test ('Roger is my dog') // sandt
/ Roger (?! Waters) /. Test ('Roger Waters er en berømt musiker') // falsk

Lookaheads bruger symbolet? = De var allerede tilgængelige.

Lookbehinds, en ny funktion, bruger? <=.

/ (? <= Roger) Waters /
/ (? <= Roger) Waters / .test ('Pink Waters is my dog') // falsk
/ (? <= Roger) Waters / .test ('Roger er min hund og Roger Waters er en berømt musiker') // sandt

En lookbehind negeres ved hjælp af?

/ (? 
/ (? 

Unicode-ejendom slipper ud \ p {...} og \ P {...}

I et almindeligt udtryksmønster kan du bruge \ d til at matche ethvert ciffer, \ n til at matche ethvert tegn, der ikke er et hvidt rum, \ w for at matche ethvert alfanumerisk tegn, og så videre.

Denne nye funktion udvider dette koncept til at omfatte alle Unicode-tegn, der introducerer \ p {} og er negation \ P {}.

Enhver unicode-karakter har et sæt egenskaber. F.eks. Bestemmer script sprogfamilien, ASCII er en boolsk, der er sand for ASCII-tegn, og så videre. Du kan placere denne egenskab i graf parenteserne, og regexet tjekker, at det er sandt:

/^\p{ASCII}+$/u.test('abc ') // 
/^\p{ASCII}+$/u.test('ABC@ ') // 
/^\p{ASCII}+$/u.test('ABC ') // 

ASCII_Hex_Digit er en anden boolsk egenskab, der kontrollerer, om strengen kun indeholder gyldige hexadecimale cifre:

/^\p{ASCII_Hex_Digit}+$/u.test('0123456789ABCDEF ') // 
/ ^\p{ASCII_Hex_Digit}+$/u.test('h ') // 

Der er mange andre boolske egenskaber, som du bare tjekker ved at tilføje deres navn i graf-parenteserne, inklusive store bogstaver, små bogstaver, White_Space, Alfabetisk, Emoji og mere:

/ ^\p{Lowercase}$/u.test('h ') // 
/ ^\p{Uppercase}$/u.test('H ') // 
/^\p{Emoji}+$/u.test('H ') // 
/^\p{Emoji}+$/u.test(' ') // 

Ud over disse binære egenskaber kan du kontrollere en af ​​unicode-karakteregenskaberne for at matche en bestemt værdi. I dette eksempel kontrollerer jeg, om strengen er skrevet i det græske eller latinske alfabet:

/^\p{Script=Greek }+$/u.test('ελληνικά ') // 
/^\p{Script=Latin}+$/u.test('hey ') // 

Læs mere om alle de egenskaber, du kan bruge direkte på forslaget.

Navngivet indfangningsgrupper

I ES2018 kan en optagegruppe tildeles et navn i stedet for blot at blive tildelt et slot i resultatsystemet:

const re = / (? <år> \ d {4}) - (?  \ d {2}) - (?  \ d {2}) /
const result = re.exec ('2015-01-02')
// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';

S flag for regelmæssige udtryk

Flaget s, forkortet for en enkelt linje, forårsager. til også at matche nye linjetegn. Uden den svarer prikken til almindelige tegn, men ikke den nye linje:

/hi.welcome/.test('hi\nwelcome ') // false
/hi.welcome/s.test('hi\nwelcome ') // sandt

ESNext

Hvad er det næste? ESNext.

ESNext er et navn, der altid angiver den næste version af JavaScript.

Den aktuelle ECMAScript-version er ES2018. Den blev frigivet i juni 2018.

Historisk JavaScript-udgaver er blevet standardiseret i løbet af sommeren, så vi kan forvente, at ECMAScript 2019 frigives i sommeren 2019.

Så i skrivende stund er ES2018 frigivet, og ESNext er ES2019

Forslag til ECMAScript-standarden er organiseret i trin. Trin 1–3 er en inkubator af nye funktioner, og funktioner, der når fase 4, afsluttes som en del af den nye standard.

På dette tidspunkt har vi en række funktioner på trin 4. Jeg vil introducere dem i dette afsnit. De nyeste versioner af de store browsere skulle allerede implementere de fleste af disse.

Nogle af disse ændringer er for det meste til internt brug, men det er også godt at vide, hvad der foregår.

Der er andre funktioner på trin 3, som muligvis kan promoveres til fase 4 i de næste par måneder, og du kan tjekke dem ud på dette GitHub-arkiv: https://github.com/tc39/proposals.

Array.prototype. {Flad, flatMap}

flat () er en ny forekomstmetode, der kan oprette en en-dimensionel matrix fra en multidimensionel matrix.

Eksempel:

['Hund', ['Får', 'Ulv']]. Flad ()
// ['Hund', 'Får', 'Ulv']

Som standard “flats” det op til et niveau, men du kan tilføje en parameter til at indstille antallet af niveauer, du vil flade arrayet til. Indstil det til Infinity for at have ubegrænsede niveauer:

['Hund', ['Får', ['Ulv']]]. Flad ()
// ['Hund', 'Får', ['Ulv']]
['Hund', ['Får', ['Ulv']]]. Flad (2)
// ['Hund', 'Får', 'Ulv']
['Hund', ['Får', ['Ulv']]]. Flad (Infinity)
// ['Hund', 'Får', 'Ulv']

Hvis du er bekendt med metoden JavaScript-kort () for en matrix, ved du, at du kan bruge en funktion til at udføre en funktion på hvert element i en matrix.

flatMap () er en ny Array-forekomstmetode, der kombinerer flad () med kort (). Det er nyttigt, når du ringer til en funktion, der returnerer en matrix i kortopkaldet (), men du vil have, at din resulterede matrix skal være flad:

['Min hund', 'er fantastisk']. Kort (ord => ord.split (''))
// [['Min', 'hund'], ['er', 'fantastisk']]
['Min hund', 'er fantastisk']. FlatMap (ord => ord.split (''))
// ['Min', 'hund', 'er', 'fantastisk']

Valgfri fangstbinding

Undertiden behøver vi ikke at have en parameter bundet til fangstblokken for en prøve / fangst.

Vi havde tidligere gjort:

prøve {
  // ...
} fangst (e) {
  // håndteringsfejl
}

Selvom vi aldrig skulle bruge e til at analysere fejlen. Vi kan nu blot udelade det:

prøve {
  // ...
} fangst {
  // håndteringsfejl
}

Object.fromEntries ()

Objekter har en indgangsmetode () siden ES2017.

Det returnerer en matrix, der indeholder alle objektets egne egenskaber, som en matrix med [nøgle, værdi] -par:

const person = {navn: 'Fred', alder: 87}
Object.entries (person) // [['navn', 'Fred'], ['alder', 87]]

ES2019 introducerer en ny Object.fromEntries () -metode, som kan oprette et nyt objekt fra en sådan række egenskaber:

const person = {navn: 'Fred', alder: 87}
const-poster = Object.entries (person)
const newPerson = Object.fromEntries (poster)

person! == newPerson // sandt

String.prototype. {TrimStart, trimEnd}

Denne funktion har været en del af v8 / Chrome i næsten et år nu, og den vil blive standardiseret i ES2019.

trimStart ()

Returner en ny streng med fjernet hvidt mellemrum fra starten af ​​den originale streng

'Testing'.trimStart () //' Testing '
'Testing'.trimStart () //' Testing '
'Testing' .trimStart () // 'Testing'
'Testing'.trimStart () //' Testing '

trimEnd ()

Returner en ny streng med fjernet hvidt rum fra slutningen af ​​den originale streng

'Testing'.trimEnd () //' Testing '
'Testing'.trimEnd () //' Testing '
'Testing' .trimEnd () // 'Testing'
'Testing' .trimEnd () // 'Testing'

Symbol.prototype.description

Du kan nu hente beskrivelsen af ​​et symbol ved at åbne dens beskrivelsesejendom i stedet for at skulle bruge toString () -metoden:

const testSymbol = Symbol ('Test')
testSymbol.description // 'Test'

JSON forbedringer

Før denne ændring var linjeseparator (\ u2028) og afsnit separator (\ u2029) symboler ikke tilladt i strenge, der blev analyseret som JSON.

Brug af JSON.parse () resulterede disse tegn i en SyntaxError, men nu analyseres de korrekt, som defineret ved JSON-standarden.

Velformet JSON.stringify ()

Fixes JSON.stringify () output, når det behandler surrogat UTF-8 kodepunkter (U + D800 til U + DFFF).

Før denne ændring ville JSON.stringify () kalde et misformet Unicode-tegn (et "�").

Nu kan disse surrogatkodepunkter sikkert repræsenteres som strenge ved hjælp af JSON.stringify () og transformeres tilbage til deres originale repræsentation ved hjælp af JSON.parse ().

Function.prototype.toString ()

Funktioner har altid haft en forekomstmetode kaldet toString (), der returnerer en streng, der indeholder funktionskoden.

ES2019 introducerede en ændring af returværdien for at undgå at strippe kommentarer og andre karakterer som whitespace, hvilket nøjagtigt repræsenterer funktionen, som den var defineret.

Hvis vi tidligere havde haft det

funktion / * dette er bar * / bar () {}

Opførslen var denne:

bar.toString () // 'funktionslinje () {}

nu er den nye adfærd:

bar.toString (); // 'funktion / * dette er bar * / bar () {}'

Når jeg pakker op, håber jeg, at denne artikel hjalp dig med at indhente nogle af de nyeste JavaScript-tilføjelser og de nye funktioner, vi ser i 2019.

Klik her for at få en PDF / ePub / Mobi-version af dette indlæg til at læse offline

Flavio