Staat in JavaScript uitgelegd door het koken van een eenvoudige maaltijd

Als u ooit thuis een maaltijd hebt gekookt, kunt u begrijpen hoe u stateful code schrijft met behulp van objectgeoriënteerde programmeermethoden in JavaScript.

Wanneer u begint met het schrijven van eenvoudige JavaScript-programma's, hoeft u zich geen zorgen te maken over het aantal variabelen dat u gebruikt, of hoe verschillende functies en objecten samenwerken.

De meeste mensen beginnen bijvoorbeeld met het gebruik van veel globale variabelen of variabelen die zich op het hoogste niveau van het bestand bevinden. Ze maken geen deel uit van een individuele klasse, object of functie.

Dit is bijvoorbeeld een globale variabele met de naam state :

let state = "global";

Maar zodra uw programma veel verschillende functies en / of objecten begint te gebruiken, moet u een strengere set regels voor uw code maken.

Dit is waar het concept van staat in het spel komt. State beschrijft de status van het hele programma of een individueel object. Het kan tekst, een getal, een booleaanse waarde of een ander gegevenstype zijn.

Het is een veelgebruikt hulpmiddel voor het coördineren van code. Als u bijvoorbeeld de status heeft bijgewerkt, kunnen een aantal verschillende functies onmiddellijk op die wijziging reageren.

Dit artikel beschrijft de status in de context van React, een populaire JavaScript-bibliotheek.

Maar Raad eens? Zelfs de staat kan u hoofdpijn bezorgen als uw code eenmaal ingewikkeld wordt! Veranderende toestand kan onbedoelde gevolgen hebben.

Laten we daar stoppen. State is een populaire tool bij objectgeoriënteerd programmeren, of OOP. Maar veel programmeurs geven de voorkeur aan functioneel programmeren, wat toestandsveranderingen ontmoedigt. JavaScript ondersteunt beide paradigma's.

Oké, dat is een heleboel terminologie tegelijk. Ik wilde een manier vinden om te laten zien hoe OOP en functioneel programmeren dezelfde doelen kunnen bereiken, zelfs als functioneel programmeren geen gebruik maakt van state.

Deze tutorial laat zien hoe je een maaltijd met spaghetti en saus kunt bereiden vanuit een OOP en vanuit een functioneel perspectief.

Hier is een korte preview van de twee verschillende benaderingen:

Laten we erin springen. Om deze zelfstudie te begrijpen, hoeft u alleen functies en objecten in JavaScript te begrijpen.

Objectgeoriënteerde methode (gebruikstoestand)

In de bovenstaande afbeelding hebben we twee verschillende benaderingen laten zien voor het maken van dit pastadiner:

  1. Een methode die gericht is op de staat van de verschillende gereedschappen, zoals de kachel, de pan en de pasta.
  2. Een methode die gericht is op de voortgang van het voedsel zelf, zonder vermelding van de staat van de individuele gereedschappen (potten, kachels etc.)

De objectgeoriënteerde benadering is gericht op het bijwerken van de status, dus onze code heeft de status op twee verschillende niveaus:

  1. Globaal, of de toestand van deze hele maaltijd.
  2. Lokaal voor elk object.

We gaan de ES6-syntaxis gebruiken in deze tutorial om objecten te maken. Hier is een voorbeeld van een wereldwijde staat en het prototype van de "Pot".

let stoveTemp = 500;
function Pot(){ this.boilStatus = ''; this.startBoiling = function(){ if( stoveTemp > 400) this.boilStatus = "boiling"; }}
let pastaPot = new Pot();pastaPot.startBoiling();
console.log(pastaPot);// Pot { boilStatus = 'boiling'; }

Opmerking: ik heb de console.logverklaring vereenvoudigd om me te concentreren op de statusupdate.

Hier is een visuele weergave van die logica:

Voordat

Na

Er zijn twee toestanden, en wanneer het pastaPotwordt gemaakt via het Potprototype, heeft het aanvankelijk een leeg boilStatus. Maar dan is er een toestandverandering.

We rennen pastaPot.startBoiling(), en nu is de boilStatus(lokale staat) aan het "koken", aangezien de wereldwijde staat stoveTempmeer dan 400 is.

Laten we nu een stap verder gaan. We laten de pasta koken door de staat van pastaPot.

Hier is de code die we aan het bovenstaande fragment zullen toevoegen:

function Pasta (){ this.cookedStatus = false; this.addToPot = function (boilStatus){ if(boilStatus == "boiling") this.cookedStatus = true; }}
let myMeal = new Pasta();myMeal.addToPot(pastaPot.boilStatus);
console.log(myMeal.cookedStatus);// true

Woah! Dat is veel tegelijk. Hier is wat er is gebeurd.

  1. We hebben een nieuw prototype van "Pasta" gemaakt, waarbij elk object een lokale staat zal hebben cookedStatus
  2. We hebben een nieuw exemplaar van Pasta gemaakt met de naam myMeal
  3. We gebruikten de toestand van het pastaPotobject die we gemaakt hebben in het laatste fragment om te bepalen of we het staat met de naam moet worden aangepast cookedStatusin myMealom gekookt.
  4. Omdat de staat van boilStatusin pastaPot"kookte", is onze pasta nu gaar!

Hier is dat proces visueel:

Voordat

Na

So, we now have the local state of one object, that depends on the local state of another object. And that local state depended on some global state! You can see how this can be challenging. But, it is at least easy to follow for now, since states are updated explicitly.

Functional Method (without state)

In order to fully understand state, you should be able to find a way to accomplish the same outcome as the code above without actually modifying state. This is where functional programming helps!

Functional programming has two core values that separate it from OOP: immutability and pure functions.

I am not going to go into too much depth on those topics, but if you want to learn more, I encourage you to check out this guide to functional programming in JavaScript.

Both of these principles discourage the use of state modification in your code. That means that we can’t use local or global state.

Functional programming instead encourages us to pass in parameters to individual functions. We can use outside variables, but we can’t use them as state.

Here’s an example of a function that will boil the pasta:

const stoveTemp = 500;
const cookPasta = (temp) => { if(temp > 400) return 'cooked';}
console.log(cookPasta(stoveTemp));// 'cooked'

This code will successfully return a string of ‘cooked’. But notice — there is no object that we are updating. The function simply returns the value that will be used in the next step.

Instead, we are focused on the inputs and outputs of one function: cookPasta.

This perspective looks at the transformation of the food itself, rather than the tools that are used to cook it. It’s a little harder to visualize, but we don’t need to have the function depend on external state.

Here’s what it looks like:

Think of it as a “timeline view” for the progress of the meal — this particular function just covers the first part, the transition from dry pasta to cooked pasta.

Now let’s cover the second part as the food is served. Here’s the code that will serve the meal. It will come after the code block above:

const serveMeal = (pasta) => { if (pasta == 'cooked') return 'Dinner is ready.'}
console.log( serveMeal(cookPasta(stoveTemp)) );// 'Dinner is ready.'

Now, we are delivering the results of the cookPasta function directly into the serveMeal function. Again, we are able to do this without changing state, or changing data structures.

Here’s a diagram that uses the “timeline view” to show how these two functions work together:

Interested In More Visual Tutorials?

If you enjoyed this guide, give it a “clap”!

And, if you would like to read more visual tutorials about HTML, CSS and JavaScript, check out the main CodeAnalogies site for 50+ tutorials.