Konstant forvirring: hvorfor jeg stadig bruger JavaScript-funktionssætninger

Tilbage i slutningen af ​​90'erne - da jeg lærte JavaScript - blev vi lært at skrive “Hello World” -funktionen ved hjælp af en funktionserklæring . Sådan her…

function helloWorld() { return ‘Hello World!’; }

Disse dage ser det ud til, at alle de seje børn skriver “Hello World” -funktionen sådan ...

const helloWorld = () => 'Hello World!';

Dette er et funktionsudtryk i ES2015 JavaScript, og det er sexet som helvede. Det er smukt at se på. Det hele er en linje. Så kortfattet. Så dejligt.

Den bruger en pilefunktion, som er en af ​​de mest populære funktioner i ES2015.

Da jeg først så dette, var jeg som:

Så efter næsten 20 års JavaScript og efter at have brugt ES2015 på en række projekter, her er hvordan jeg ville skrive “Hello World” -funktionen i dag:

function helloWorld() { return ‘Hello World!’; }

Nu hvor jeg har vist dig den nye måde, er jeg sikker på, at du næppe kan se på den gamle skolekode ovenfor.

Tre hele linjer for bare en simpel lille funktion! Alle de ekstra tegn!

Jeg ved hvad du tænker ...

Jeg elsker pilfunktioner, det gør jeg virkelig. Men når jeg har brug for at erklære en topfunktion i min kode, bruger jeg stadig en god gammeldags funktionserklæring.

Dette citat fra "Onkel Bob" Martin forklarer hvorfor:

“... forholdet mellem tid brugt på læsning og skrivning er langt over 10 til 1. Vi læser konstant gammel kode som en del af bestræbelserne på at skrive ny kode.

Fordi dette forhold er så højt, ønsker vi, at kodelæsningen skal være let, selvom det gør skrivningen sværere. ”

- Robert C. Martin

Ren kode: En håndbog om agil softwarehåndværk

Funktionssætninger har to klare fordele i forhold til funktionsudtryk:

Fordel nr. 1: klarhed i hensigt

Når du scanner gennem tusinder af linjer med kode om dagen, er det nyttigt at kunne finde ud af programmørens hensigt så hurtigt og nemt som muligt.

Se på dette:

const maxNumberOfItemsInCart = ...;

Du læser alle disse tegn, og du ved stadig ikke, om ellipsen repræsenterer en funktion eller en anden værdi. Det kunne være:

const maxNumberOfItemsInCart = 100;

... eller det kan lige så let være:

const maxNumberOfItemsInCart = (statusPoints) => statusPoints * 10;

Hvis du bruger en funktionserklæring, er der ingen sådan tvetydighed.

Se på:

const maxNumberOfItemsInCart = 100;

…imod:

function maxNumberOfItemsInCart(statusPoints) { return statusPoints * 10; }

Hensigten er krystalklar lige fra start af linjen.

Men måske bruger du en kodeeditor, der har nogle farvekodende spor. Måske er du en hurtiglæser. Måske tror du bare ikke, det er så meget.

Jeg hører dig. Spændingen ser stadig ret sexet ud.

Faktisk, hvis dette var min eneste grund, kunne jeg have fundet en måde at overbevise mig selv om, at det er en værdifuld afvejning.

Men det er ikke min eneste grund ...

Fordel nr. 2: Bekendtgørelsesrækkefølge == ordre på udførelse

Ideelt set vil jeg erklære min kode mere eller mindre i den rækkefølge, som jeg forventer, at den bliver udført.

Dette er showstopper for mig: enhver værdi, der erklæres ved hjælp af const-nøgleordet, er utilgængelig, indtil udførelsen når den.

Retfærdig advarsel: Jeg er ved at gå alt, "Professor JavaScript" til dig. Det eneste du skal forstå i al jargon nedenfor er, at du ikke kan bruge en konst, før du har erklæret det .

Følgende kode kaster en fejl:

sayHelloTo(‘Bill’); const sayHelloTo = (name) => `Hello ${name}`;

Dette skyldes, at når JavaScript-motoren læser koden, binder den "sayHelloTo", men den initialiseres ikke.

Alle erklæringer i JavaScript er bundet tidligt, men de initialiseres forskelligt.

Med andre ord, JavaScript binder erklæringen om "sayHelloTo" - læser den først og skaber et rum i hukommelsen til at holde dens værdi - men den indstiller ikke "sayHelloTo" til noget, før den når den under udførelse .

Tiden mellem "sayHelloTo" er bundet og "sayHelloTo" initialiseres kaldes den tidsmæssige døde zone (TDZ).

Hvis du bruger ES2015 direkte i browseren (i modsætning til transpilering ned til ES5 med noget som Babel), kaster følgende kode faktisk også en fejl:

if(thing) { console.log(thing); } const thing = 'awesome thing';

The code above, written using “var” instead of “const”, would not throw an error because vars get initialized as undefined when they are bound, whereas consts are not initialized at all at bind time. But I digress…

Function statements do not suffer from this TDZ problem. The following is perfectly valid:

sayHelloTo(‘Bill’); function sayHelloTo(name) { return `Hello ${name}`; }

This is because function statements get initialized as soon as they are bound — before any code is executed.

So, no matter when you declare the function, it will be available to its lexical scope as soon as the code starts executing.

What I’ve just described above forces us to write code that looks upside down. We have to start with the lowest level function and work our way up.

My brain doesn’t work that way. I want the context before the details.

Most code is written by humans. So it makes sense that most people’s order of understanding roughly follows most code’s order of execution.

In fact, wouldn’t it be nice if we could provide a little summary of our API at the top of our code? With function statements, we totally can.

Check out this (somewhat contrived) shopping cart module…

export { createCart, addItemToCart, removeItemFromCart, cartSubTotal, cartTotal, saveCart, clearCart, } function createCart(customerId) {...} function isValidCustomer(customerId) {...} function addItemToCart(item, cart) {...} function isValidCart(cart) {...} function isValidItem(item) {...} ...

With function expressions it would look something like…

... const _isValidCustomer = (customerId) => ... const _isValidCart = (cart) => ... const _isValidItem = (item) => ... const createCart = (customerId) => ... const addItemToCart = (item, cart) => ... ... export { createCart, addItemToCart, removeItemFromCart, cartSubTotal, cartTotal, saveCart, clearCart, }

Imagine this as a larger module with many small internal functions. Which would you prefer?

There are those who will argue that using something before you’ve declared it is unnatural, and can have unintended consequences. There are even extremely smart people who have said such things.

It is definitely an opinion — not a fact — that one way is better than the other.

But if you ask me: Code is communication. Good code tells a story.

I’ll let the compilers and the transpilers, the minifiers and the uglyfiers, deal with optimizing code for the machines.

I want to optimize my code for human understanding.

What about those arrow functions, though?

Yes. Still sexy and still awesome.

I typically use arrow functions to pass a small function as a value to a higher order function. I use arrow functions with promises, with map, with filter, with reduce. They are the bees knees, my friends!

Some examples:

const goodSingers = singers.filter((singer) => singer.name !== 'Justin Bieber'); function tonyMontana() { return getTheMoney() .then((money) => money.getThePower()) .then((power) => power.getTheWomen()); }

I used a few other new JavaScript features in this article. If you want to learn more about the latest JavaScript standard (ES2015) and all the cool features it has to offer, you should get my quick start guide for free.

Mit mål er altid at hjælpe så mange udviklere som muligt. Hvis du fandt denne artikel nyttig, skal du trykke på knappen ❤ (anbefale), så andre kan se den. Tak!