Stalna zbrka: zašto i dalje koristim izjave JavaScript funkcije

Još krajem 90-ih - kad sam naučio JavaScript - naučili su nas pisati funkciju "Hello World" koristeći izjavu funkcije . Kao ovo…

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

Ovih dana izgleda da sva cool djeca ovako pišu funkciju "Hello World" ...

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

Ovo je izraz funkcije u ES2015 JavaScriptu i vraški je seksi. Prekrasno je gledati. Sve je to jedna linija. Tako kratko. Tako ljupko.

Koristi funkciju strelice koja je jedna od najpopularnijih značajki ES2015.

Kad sam prvi put vidio ovo, bio sam kao:

Dakle, nakon gotovo 20 godina JavaScript-a i nakon upotrebe ES2015 na brojnim projektima, evo kako bih danas napisao funkciju „Hello World“:

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

Sad kad sam vam pokazao novi način, siguran sam da jedva podnosite pogled na stari školski kod gore.

Tri cijele linije za samo jednostavnu malu funkciju! Svi ti dodatni likovi!

Znam o čemu razmišljaš ...

Volim funkcije strelica, stvarno ih volim. Ali kada u svom kodu trebam proglasiti funkciju najviše razine, i dalje koristim dobru staromodnu izjavu funkcije.

Ovaj citat "Ujaka Boba" Martina objašnjava zašto:

“... omjer vremena provedenog u čitanju naspram pisanja znatno je veći od 10 prema 1. Stalno čitamo stari kôd kao dio napora da napišemo novi kôd.

Budući da je ovaj omjer tako visok, želimo da čitanje koda bude lako, čak i ako otežava pisanje. "

- Robert C. Martin

Čisti kôd: Priručnik za agilno izrađivanje softvera

Izjave funkcije imaju dvije jasne prednosti u odnosu na izraze funkcija:

Prednost # 1: Jasnoća namjere

Kada skenirate kroz tisuće redaka koda dnevno, korisno je što brže i lakše shvatiti namjeru programera.

Pogledaj ovo:

const maxNumberOfItemsInCart = ...;

Pročitali ste sve te znakove i još uvijek ne znate predstavlja li elipsa funkciju ili neku drugu vrijednost. To bi mogao biti:

const maxNumberOfItemsInCart = 100;

... ili isto tako lako može biti:

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

Ako upotrebljavate izraz funkcije, nema takve dvosmislenosti.

Pogledaj:

const maxNumberOfItemsInCart = 100;

…protiv:

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

Namjera je kristalno jasna od početka retka.

Ali možda koristite uređivač koda koji ima neke naznake kodiranja boja. Možda ste čitač brzine. Možda jednostavno ne misliš da je to tako velika stvar.

Čujem te. Lakoća i dalje izgleda prilično seksi.

Zapravo, da je to moj jedini razlog, možda bih pronašao način da se uvjerim da je to vrijedan kompromis.

Ali to nije moj jedini razlog ...

Prednost # 2: Redoslijed izjave == redoslijed izvršenja

U idealnom slučaju, želim svoj kôd deklarirati manje-više onim redoslijedom za koji očekujem da će se izvršiti.

Ovo je za mene showstopper: bilo koja vrijednost deklarirana pomoću ključne riječi const je nepristupačna dok je izvršenje ne dostigne.

Pošteno upozorenje: Uskoro ću krenuti sa svim, "profesore JavaScript" na vas. Jedino što u svim donjim žargonima morate razumjeti je da ne možete koristiti const dok ga ne proglasite .

Sljedeći će kôd izbaciti pogrešku:

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

To je zato što će, kada JavaScript motor pročita kôd, vezati "sayHelloTo", ali ga neće inicijalizirati .

Sve deklaracije u JavaScript-u rano su uvezane, ali se različito inicijaliziraju.

Drugim riječima, JavaScript veže deklaraciju "sayHelloTo" - prvo je čita i stvara prostor u memoriji da zadrži njezinu vrijednost - ali "sayHelloTo" ne postavlja ni na što dok je ne postigne tijekom izvršenja .

Vrijeme između vezanja "sayHelloTo" i inicijalizacije "sayHelloTo" naziva se vremenska mrtva zona (TDZ).

Ako ES2015 upotrebljavate izravno u pregledniku (za razliku od prebacivanja na ES5 nečim poput Babela), sljedeći kôd zapravo također donosi pogrešku:

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.

My goal is always to help as many developers as possible, if you found this article useful, please hit the ❤ (recommend) button so that others will see it. Thanks!