Állandó zavartság: miért használom továbbra is a JavaScript függvény utasításokat

Még a 90-es évek végén - amikor megtanultam a JavaScript-et - megtanítottak minket írni a „Hello World” függvényt egy függvény utasítás segítségével . Mint ez…

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

Manapság úgy tűnik, hogy a menő gyerekek így írják a „Hello World” funkciót ...

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

Ez egy funkciókifejezés az ES2015 JavaScript-ben, és pokolian szexi. Gyönyörű nézni. Mindez egy vonal. Olyan szűkös. Olyan aranyos.

Nyílfüggvényt használ, amely az ES2015 egyik legnépszerűbb jellemzője.

Amikor először láttam, olyan voltam, mint:

Szóval, majdnem 20 évnyi JavaScript után, és miután számos projektben használtam az ES2015-et, íme, hogyan írnám ma a „Hello World” funkciót:

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

Most, hogy megmutattam neked az új utat, biztos vagyok benne, hogy alig bírsz megnézni a fenti régi iskola kódját.

Három egész sor csak egy egyszerű kis funkcióhoz! Az összes extra karakter!

Tudom, mire gondolsz ...

Szeretem a nyíl funkciókat, nagyon is. De amikor a legfelső szintű függvényt kell deklarálnom a kódomban, akkor is jó régimódi függvény-állítást használok.

Martin „Bob bácsi” idézete megmagyarázza, miért:

„… Az olvasással és az írással töltött idő aránya jóval meghaladja a 10-et. Az új kód megírásának részeként folyamatosan régi kódot olvasunk.

Mivel ez az arány olyan magas, azt akarjuk, hogy a kód olvasása akkor is egyszerű legyen, ha ez megnehezíti az írást. ”

- Robert C. Martin

Tiszta kód: Az agilis szoftver kézműves kézikönyve

A függvény-utasításoknak két egyértelmű előnye van a függvény-kifejezésekkel szemben:

1. előny: A szándék egyértelműsége

Napi több ezer sornyi kód beolvasásakor hasznos, ha a lehető leggyorsabban és könnyebben kitalálja a programozó szándékát.

Ezt nézd meg:

const maxNumberOfItemsInCart = ...;

Elolvasta az összes karaktert, és még mindig nem tudja, hogy az ellipszis függvényt vagy más értéket képvisel-e. Lehet, hogy:

const maxNumberOfItemsInCart = 100;

… Vagy ugyanolyan könnyen lehet:

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

Ha egy függvény utasítást használ, akkor nincs ilyen kétértelműség.

Megnézi:

const maxNumberOfItemsInCart = 100;

…ellen:

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

A szándék már a sor elejétől kristálytiszta.

De lehet, hogy használ egy kódszerkesztőt, amely színkódolási nyomokkal rendelkezik. Talán gyorsolvasó vagy. Talán csak nem gondolja, hogy ez olyan nagy baj.

Hallak téged. A szűkszavúság még mindig elég szexi.

Valójában, ha ez lenne az egyetlen okom, talán megtalálhattam volna a módját, hogy meggyőzzem magam arról, hogy ez egy értékes kompromisszum.

De ez nem az egyetlen ok ...

Előny # 2: Nyilatkozati sorrend == végrehajtási sorrend

Ideális esetben a kódomat többé-kevésbé abban a sorrendben szeretném deklarálni, mint amire számítok.

Ez számomra a showstopper: az const kulcsszóval deklarált értékek nem érhetők el, amíg a végrehajtás el nem éri.

Tisztességes figyelmeztetés: Mindent megteszek, „JavaScript professzor”. Az egyetlen dolog, amit meg kell értenie az alábbi szakzsargonban, az az, hogy nem használhat egy konstelt addig, amíg ki nem nyilatkozta .

A következő kód hibát okoz:

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

Ez azért van, mert amikor a JavaScript motor elolvassa a kódot, akkor a „sayHelloTo” -t fogja kötni , de nem inicializálja .

Az összes JavaScript-deklaráció korán be van kötve, de inicializálása eltérõ.

Más szóval, a JavaScript kötődik a nyilatkozat „sayHelloTo” - szól először, és létrehoz egy hely a memóriában, hogy tartja értékét - de nem meghatározott „sayHelloTo”, hogy semmit, amíg el nem éri azt közben végrehajtás .

A „sayHelloTo” megkötése és a „sayHelloTo” inicializálása közötti időt időbeli holt zónának (TDZ) nevezzük .

Ha az ES2015-et közvetlenül a böngészőben használja (szemben az ES5-re való áttelepítésével valami hasonlóval, mint a Babel), akkor a következő kód is hibát dob:

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!