Lad os afmystificere JavaScript's 'nye' nøgleord

I løbet af weekenden gennemførte jeg Will Sentances JavaScript: The Hard Parts. Det lyder måske ikke som den mest strålende måde at tilbringe en weekend på, men jeg fandt det faktisk ret sjovt og afslappende at gennemføre kurset. Det berørte funktionel programmering, højere ordensfunktioner, lukninger og asynkron JavaScript.

For mig var kursets højdepunkt, hvordan han udvidede JavaScript-tilgangen til objektorienteret programmering (OOP) og demystificerede magien bag den nye operatør. Jeg har nu en velafrundet forståelse af, hvad der foregår under emhætten, når den nye operatør bruges.

Objektorienteret programmering i JavaScript

Object-Oriented Programming (OOP) er et programmeringsparadigme baseret på begrebet "objekter". Data og funktioner (attributter og metoder) er samlet i et objekt.

Et objekt i JavaScript er en samling af nøgleværdipar. Disse nøgleværdipar er objektets egenskaber. En egenskab kan være en matrix, en funktion, et objekt selv eller enhver primitiv datatype såsom strenge eller heltal.

Hvilke teknikker har vi i vores JavaScript-værktøjskasse til oprettelse af objekter?

Lad os antage, at vi opretter brugere i et spil, som vi lige har designet. Hvordan vil vi gemme brugeroplysninger såsom deres navne, punkter og implementere metoder såsom en stigning i point? Her er to muligheder for grundlæggende oprettelse af objekter.

Mulighed 1 - Objekt bogstavelig notation

let user1 = { name: "Taylor", points: 5, increment: function() { user1.points++; } };

Et bogstaveligt JavaScript-objekt er en liste over navn-værdi-par indpakket i krøllede seler. I eksemplet ovenfor oprettes objektet 'bruger1', og de tilknyttede data lagres i det.

Mulighed 2 - Object.create ()

Object.create(proto, [ propertiesObject ])

Object.create metoder accepterer to argumenter:

  1. proto: objektet, der skal være prototypen for det nyoprettede objekt. Det skal være et objekt eller null.
  2. propertiesObject: egenskaber for det nye objekt. Dette argument er valgfrit.

Dybest set passerer du ind i Object.createet objekt, som du vil arve fra, og det returnerer et nyt objekt, der arver fra det objekt, du sendte til det.

let user2 = Object.create(null); user2.name = "Cam"; user2.points = 8; user2.increment = function() { user2.points++; }

De grundlæggende muligheder for oprettelse af objekter ovenfor er gentagne. Det kræver, at hver enkelt oprettes manuelt.

Hvordan overvinder vi dette?

Løsninger

Løsning 1 - Generer objekter ved hjælp af en funktion

En enkel løsning er at skrive en funktion for at oprette nye brugere.

function createUser(name, points) { let newUser = {}; newUser.name = name; newUser.points = points; newUser.increment = function() { newUser.points++; }; return newUser; }

For at oprette en bruger skal du nu indtaste oplysningerne i funktionens parametre.

let user1 = createUser("Bob", 5); user1.increment();

Inkrementeringsfunktionen i eksemplet ovenfor er dog kun en kopi af den oprindelige inkrementfunktion. Dette er ikke en god måde at skrive din kode på, da eventuelle ændringer i funktionen skal udføres manuelt for hvert objekt.

Løsning 2 - Brug JavaScript-prototypen

I modsætning til objektorienterede sprog som Python og Java har JavaScript ikke klasser. Det bruger begrebet prototyper og prototypekæde til arv.

Når du opretter en ny matrix, du automatisk har adgang til indbyggede metoder som Array.join, Array.sortog Array.filter. Dette skyldes det faktum, at array-objekter arver egenskaber fra Array.prototype.

Hver JavaScript-funktion har en prototype-egenskab, som er tom som standard. Du kan tilføje funktioner til denne prototype-egenskab, og i denne form er den kendt som en metode. Når en arvet funktion udføres, peger værdien af ​​dette på det arvende objekt.

function createUser(name, points) { let newUser = Object.create(userFunction); newUser.name = name; newUser.points = points; return newUser; } let userFunction = { increment: function() {this.points++}; login: function() {console.log("Please login.")}; } let user1 = createUser("Bob", 5); user1.increment();

Da user1objektet blev oprettet, blev der dannet en prototype-kædebinding med userFunction.

Når der user1.increment() er i opkaldstakken, vil tolken lede efter bruger1 i den globale hukommelse. Derefter ser den efter stigningsfunktionen, men finder den ikke. Tolken vil se på det næste objekt op ad prototypekæden og vil finde inkrementfunktionen der.

Løsning 3 - nye og disse nøgleord

Detny operator bruges til at oprette en forekomst af et objekt, der har en konstruktorfunktion.

Når vi kalder konstruktorfunktionen med ny, automatiserer vi følgende handlinger:

  • Et nyt objekt oprettes
  • Det binder sig thistil objektet
  • Konstruktorfunktionens prototype-objekt bliver __proto__-egenskaben for det nye objekt
  • Det returnerer objektet fra funktionen

Dette er fantastisk, fordi automatiseringen resulterer i mindre gentagen kode!

function User(name, points) { this.name = name; this.points = points; } User.prototype.increment = function(){ this.points++; } User.prototype.login = function() { console.log(“Please login.”) } let user1 = new User(“Dylan”, 6); user1.increment();

Ved at bruge prototypemønsteret tilføjes hver metode og egenskab direkte på objektets prototype.

Tolken vil gå op i prototypekæden og finde stigningsfunktionen under brugerens prototypeegenskab, som i sig selv også er et objekt med oplysningerne inde i det. Husk - Alle funktioner i JavaScript er også objekter . Nu hvor tolken har fundet det, den har brug for, kan den oprette en ny lokal udførelseskontekst, der skal køres user1.increment().

Sidebemærkning: Forskel mellem __proto__ og prototype

Hvis du allerede bliver forvirret over __proto__ og prototype, skal du ikke bekymre dig! Du er langt fra den eneste, der er forvirret over dette.

Prototype er en egenskab for konstruktorfunktionen, der bestemmer, hvad der bliver egenskaben __proto__ på det konstruerede objekt.

Så __proto__ er den oprettede reference, og denne reference er kendt som prototypekædebindingen.

Løsning 4 - ES6 'syntaktisk sukker'

Andre sprog tillader os at skrive vores delte metoder inden for selve objektet "konstruktør". ECMAScript6 introducerede klassens nøgleord, som giver os mulighed for at skrive klasser, der ligner normale klasser af andre klassiske sprog. I virkeligheden er det syntaktisk sukker over JavaScript's prototypiske opførsel.

class User { constructor(name, points) { this.name = name; this.points = points; } increment () { this.points++; } login () { console.log("Please login.") } } let user1 = new User("John", 12); user1.increment();

I løsning 3 blev de tilknyttede metoder nøjagtigt implementeret ved hjælp af User.prototype.functionName. I denne løsning opnås de samme resultater, men syntaksen ser renere ud.

Konklusion

Vi har nu lært mere om de forskellige muligheder, vi har i JavaScript til at oprette objekter. Mens klassedeklarationer ognye operatører er relativt lette at bruge, er det vigtigt at forstå, hvad der er automatiseret.

For at opsummere automatiseres følgende handlinger, når konstruktorfunktionen kaldes med ny :

  • Et nyt objekt oprettes
  • Det binder sig thistil objektet
  • Konstruktorfunktionens prototype-objekt bliver __proto__-egenskaben for det nye objekt
  • Det returnerer objektet fra funktionen

Tak, fordi du læste min artikel, og klapp, hvis du kunne lide det! Tjek mine andre artikler som hvordan jeg byggede min Pomodoro Clock-app og de lektioner, jeg lærte undervejs.