Pro a proti „nechme“.

V tomto příspěvku se budu zabývat případem (a možná proti?) jedné z nových funkcí přicházejících v JavaScript ES6:let klíčové slovo. let umožňuje novou formu stanovení rozsahu, která nebyla dříve obecně přístupná vývojářům JS:block scoping .

Rozsah funkcí

Pojďme si stručně zopakovat základy stanovení rozsahu funkcí – pokud potřebujete podrobnější pokrytí, podívejte se na můj „You Don't Know JS:Rozsah a uzávěry“ kniha, která je součástí „You Don't Know JS“ knižní série.

Zvažte:

foo();    // 42

function foo() {
    var bar = 2;
    if (bar > 1 || bam) {
        var baz = bar * 10;
    }

    var bam = (baz * 2) + 2;

    console.log( bam );
}

Možná jste již slyšeli výraz „zvedání“, který popisuje, jak JS var s prohlášeními se zachází v rámci rozsahů. Není to přesně technický popis toho, jak to funguje, ale spíše metafora. Ale pro naše účely je to dost dobré pro ilustraci. S výše uvedeným úryvkem se v podstatě zachází, jako by byl napsán takto:

function foo() {
    var bar, baz, bam;

    bar = 2;

    if (bar > 1 || bam) {
        baz = bar * 10;
    }

    bam = (baz * 2) + 2;

    console.log( bam );
}

foo();  // 42

Jak můžete vidět, foo() deklarace funkce byla přesunuta (aka "hoisted", aka zvednutá) na vrchol svého rozsahu a podobně bar , baz a bam proměnné byly zvednuty na vrchol svého rozsahu.

Protože se proměnné JS vždy chovaly tímto způsobem, mnoho vývojářů se rozhodlo automaticky vložit své var deklarace v horní části každého rozsahu (funkce), aby odpovídaly stylu kódu jeho chování. A je to naprosto platný způsob, jak věci řešit.

Ale viděli jste někdy kód, který to dělá, ale také bude dělat věci jako toto ve stejném kódu:

for (var i=0; i<10; i++) {
    // ..
}

To je také velmi běžné. Další příklad, který je poměrně běžný:

var a, b;

// other code

// later, swap `a` and `b`
if (a && b) {
    var tmp = a;
    a = b;
    b = tmp;
}

var tmp uvnitř if block sorta porušuje zdánlivý styl kódování „přesunout všechny deklarace na začátek“. Stejné jako var i v for smyčka v předchozím úryvku.

V obou případech se proměnné tak jako tak „zvedají“, tak proč vývojáři stále dávají tyto deklarace proměnných hlouběji do rozsahu, nikoli na začátek, zvláště pokud všechny ostatní deklarace již byly ručně přesunuty?

Rozsah bloku

Nejvýraznějším důvodem je to, že vývojáři (často instinktivně) chtějí nějaké proměnné, aby se chovaly, jako by patřily do menší, omezenější části rozsahu. Konkrétně existují případy, kdy chceme zahrnout deklaraci proměnné do bloku že je to spojeno výhradně s.

V for (var i=..) .. Je téměř univerzální, že vývojář zamýšlí pro i být používán pouze pro účely této smyčky, nikoli mimo ni. Jinými slovy, vývojář vkládá var i prohlášení v for smyčka, která stylisticky signalizuje všem ostatním – a jejich budoucímu já! -- že i patří do for pouze smyčka. Totéž s var tmp uvnitř toho if tvrzení. tmp je dočasná proměnná a existuje pouze pro účely if blokovat.

Stylisticky říkáme:„nepoužívejte proměnnou nikde jinde než právě tady“.

Princip nejmenšího privilegia

Existuje softwarové inženýrství nazývané „princip nejmenšího privilegia (nebo expozice)“, které naznačuje, že správný návrh softwaru skrývá detaily, pokud a dokud není nutné je odhalit. Často to děláme přesně to v návrhu modulu, skrytím soukromých proměnných a funkcí uvnitř uzávěru a vystavením pouze menší podmnožiny funkcí/vlastností jako veřejné API.

Block scoping je rozšířením stejného způsobu myšlení. To, co navrhujeme, je, že správný software umístí proměnné co nejblíže a co nejdále v rozsahu/blokování tam, kde budou použity.

Tento přesný princip již instinktivně znáte. Už víte, že neděláme všechny proměnné globálními, i když v některých případech by to bylo snazší . Proč? Protože je to špatný design. Je to návrh, který povede k (neúmyslným) kolizím, které povedou k chybám .

Takže své proměnné vložíte do funkce, kterou používají. A když vnoříte funkce do jiných funkcí, vnoříte proměnné do těchto vnitřních funkcí, jak je to nutné a vhodné. A tak dále.

Block scoping jednoduše říká, chci být schopen ošetřit { .. } blok jako obor, aniž byste museli vytvářet novou funkci k zapouzdření tohoto oboru.

Dodržujete zásadu, když říkáte:„Pokud budu používat pouze i pro tento for smyčky, dám to správně do for definice smyčky."

JS chybějící rozsah bloku

Bohužel, JS historicky neměl žádný praktický způsob, jak jej vynutit Tento styl určování rozsahu, takže bylo na nejlepším chování respektovat signalizovaný styl. Nedostatek prosazování samozřejmě znamená, že tyto věci jsou porušovány a někdy je to v pořádku, zatímco jindy to vede k chybám.

Jiné jazyky (např. Java, C++) mají skutečný rozsah bloku , kde můžete deklarovat, že proměnná patří do konkrétního bloku namísto do okolního rozsahu/funkce. Vývojáři z těchto jazyků dobře znají výhody používání blokového rozsahu pro některé jejich prohlášení.

Často mají pocit, že JS postrádá vyjadřovací schopnosti, protože chybí způsob, jak vytvořit inline rozsah v rámci { .. } blok místo definice těžší vložené funkce (známé také jako IIFE – Immediately Invoked Function Expression).

A mají naprostou pravdu. JavaScriptu chybí rozsah bloku. Konkrétně nám chybí syntaktický způsob, jak prosadit to, co nám již vyhovuje stylisticky vyjádřit.

Ne všechno

Dokonce i v jazycích, které mají blokový rozsah, ne každá deklarace proměnné skončí blokovým rozsahem.

Vezměte si jakýkoli dobře napsaný kód z takového jazyka a určitě najdete nějaké deklarace proměnných, které existují na úrovni funkcí, a jiné, které existují na úrovních menších bloků. Proč?

Protože to je přirozený požadavek toho, jak píšeme software. Někdy máme proměnnou, kterou budeme používat všude ve funkci, a někdy máme proměnnou, kterou budeme používat jen na velmi omezeném místě. Rozhodně to není návrh typu všechno nebo nic.

Důkaz? Funkční parametry. To jsou proměnné, které existují pro celý rozsah funkce. Pokud je mi známo, nikdo vážně neprosazuje myšlenku, že funkce by neměly mít explicitní pojmenované parametry, protože by nebyly „rozsahem bloku“, protože většina rozumných vývojářů ví, co zde tvrdím:

Rozsah bloku a rozsah funkce jsou platné a oba užitečné , nejen jedno nebo druhé. Tento druh kódu by byl docela hloupý:

function foo() {    // <-- Look ma, no named parameters!
    // ..

    {
        var x = arguments[0];
        var y = arguments[1];

        // do something with `x` and `y`
    }

    // ..
}

Téměř jistě byste takový kód nenapsali, jen abyste měli mentalitu "pouze blokový rozsah" o struktuře kódu, víc než byste měli x a y být globálními proměnnými v mentalitě „pouze globální rozsah“.

Ne, stačí pojmenovat x a y parametry a použijte je kdekoli ve funkci, kterou potřebujete.

Totéž by platilo pro jakékoli jiné deklarace proměnných, které byste mohli vytvořit a které chcete a potřebujete použít v celé funkci. Pravděpodobně byste vložili var v horní části funkce a pokračujte.

Představujeme let

Nyní, když chápete, proč je rozsah blokování důležitý a důležitý spolkl kontrolu zdravého rozumu, že spíše mění funkci/globální rozsah, než aby je nahradil, můžeme být nadšeni, že ES6 je konečně zavedení přímého mechanismu pro stanovení rozsahu bloku pomocí let klíčové slovo.

Ve své nejzákladnější podobě let je sourozenec var . Ale deklarace provedené pomocí let jsou omezeny na bloky, ve kterých se vyskytují, spíše než aby byly "zvednuty" do rozsahu obklopující funkce jako var s do:

function foo() {
    a = 1;                  // careful, `a` has been hoisted!

    if (a) {
        var a;              // hoisted to function scope!
        let b = a + 2;      // `b` block-scoped to `if` block!

        console.log( b );   // 3
    }

    console.log( a );       // 1
    console.log( b );       // ReferenceError: `b` is not defined
}

Hurá! let deklarace nejen vyjadřují, ale také prosazují blokový rozsah!

V zásadě se blok vyskytuje na jakémkoli místě (například { .. } pár), let můžete v něm vytvořit deklaraci s rozsahem bloku. Kdekoli tedy potřebujete vytvořit deklarace s omezeným rozsahem, použijte let .

Poznámka: Ano, let neexistuje před ES6. Existuje však poměrně málo transpilátorů ES6-ES5 – například:traceur, 6to5 a Continuum – které zaberou váš ES6 let použití (spolu s většinou zbytku ES6!) a převeďte jej na kód ES5 (a někdy ES3), který poběží ve všech relevantních prohlížečích. „Novým normálem“ ve vývoji JS, vzhledem k tomu, že JS se začne rychle vyvíjet na bázi funkce po funkci, je použití takových transpilerů jako standardní součást vašeho procesu sestavování. To znamená, že byste měli začít vytvářet nejnovější a nejlepší JS právě teď a nechte nástroje, aby se staraly o to, aby to fungovalo ve (starších) prohlížečích. Už byste se neměli vzdávat nových jazykových funkcí na roky, dokud nezmizí všechny předchozí prohlížeče.

Implicitní vs. Explicitní

Je snadné se ztratit ve vzrušení let že jde o implicitní rozsah mechanismus. Unese existující blok a přidá k původnímu účelu tohoto bloku také sémantika bytí rozsahu.

if (a) {
    let b = a + 2;
}

Zde je blok if blok, ale let pouhá přítomnost uvnitř znamená, že se blok stává také rozsahem. Jinak, pokud let nebyl tam, { .. } blok není rozsah .

Proč na tom záleží?

Vývojáři obecně preferují explicitní mechanismy spíše než implicitní mechanismy , protože to obvykle usnadňuje čtení, pochopení a údržbu kódu.

Například v oblasti nátlaku typu JS by mnoho vývojářů preferovalo explicitní nátlak přes implicitní nátlak :

var a = "21";

var b = a * 2;          // <-- implicit coercion -- yuck :(
b;                      // 42

var c = Number(a) * 2;  // <-- explicit coercion -- much better :)
c;                      // 42

Poznámka: Chcete-li si přečíst více o tomto vedlejším tématu implicitního/explicitního nátlaku, přečtěte si můj „You Don't Know JS:Types &Grammar“ kniha, konkrétně Kapitola 4:Nátlak.

Když příklad ukazuje blok s pouze jedním nebo několika řádky kódu uvnitř, je poměrně snadné zjistit, zda je blok omezen nebo ne:

if (a) {    // block is obviously scoped
    let b;
}

Ale ve více reálných scénářích může mít jeden blok často desítky řádků kódu, možná dokonce sto nebo více. Ponecháme-li stranou preference/názor, že takové bloky by neměly existovat – existují , je to realita -- pokud let je pohřben hluboko uprostřed všeho toho kódu, je to mnohem těžší abyste věděli, zda je daný blok rozsahem či nikoli.

A naopak, pokud najdete let deklaraci někde v kódu a chcete vědět, do kterého bloku patří, místo abyste jen vizuálně skenovali nahoru na nejbližší function klíčové slovo, nyní musíte vizuálně skenovat na nejbližší { otevírací složená závorka. To je těžší. Ne moc těžší, ale přesto těžší.

Je to trochu větší mentální daň.

Implicitní nebezpečí

Ale není to jen duševní daň. Zatímco var deklarace jsou "vyzvednuty" na začátek uzavírací funkce let deklarace nejsou považovány za „vyzvednuté“ na začátek bloku. Pokud se omylem pokusíte použít proměnnou s rozsahem bloku v bloku dříve než tam, kde jeho deklarace existuje, zobrazí se chyba:

if (a) {
    b = a + 2;      // ReferenceError: `b` is not defined

    // more code

    let b = ..

    // more code
}

Poznámka: Doba "času" mezi otevřením { a kde let b Zdá se, že se technicky nazývá „temporální mrtvá zóna“ (TDZ) – to si nevymýšlím! -- a proměnné nelze použít v jejich TDZ. Technicky má každá proměnná svůj vlastní TDZ a jaksi se překrývají, opět od otevření bloku po oficiální deklaraci/inicializaci.

Protože jsme dříve vložili let b = .. deklarace dále v bloku, a pak jsme se chtěli vrátit a použít ji dříve v bloku, máme nebezpečí - footgun - kde jsme zapomněli, že musíme najít let klíčové slovo a přesuňte jej na nejstarší použití b proměnná.

S největší pravděpodobností se vývojáři touto „chybou“ TDZ nechají kousnout a nakonec se z této špatné zkušenosti poučí a vždy uvedou své let deklarace v horní části bloku.

A je tu ještě jedno nebezpečí let rozsah:nebezpečí refaktorizace.

Zvažte:

if (a) {
    // more code

    let b = 10;

    // more code

    let c = 1000;

    // more code

    if (b > 3) {
        // more code

        console.log( b + c );

        // more code
    }

    // more code
}

Řekněme později, že si uvědomíte if (b > 3) část kódu je třeba přesunout mimo if (a) { .. blokovat, z jakéhokoli důvodu. Uvědomíte si, že také potřebujete chytit let b = .. prohlášení, že se s tím pohnete.

Ale okamžitě si neuvědomíte, že blok spoléhá na c také - protože je to trochu více skryté v kódu - a to c má blokový rozsah na if (a) { .. blok. Jakmile přesunete if (b > 3) { .. blok, nyní se kód zlomí a vy musíte najít let c = .. deklaraci a zjistit, zda se může pohybovat atd.

Mohl bych stále vymýšlet další scénáře - hypotetické ano, ale také velmi poučené mnoha zkušenostmi nejen s mým vlastním, ale s cizím vlastním kódem reálného světa - ale myslím, že rozumíte. Je strašně snadné dostat se do těchto nebezpečných pastí.

Pokud by existovaly explicitní rozsahy pro b a c , pravděpodobně by bylo o něco snazší zjistit, jaký refaktoring je nezbytný, než klopýtat a zjišťovat to implicitně .

Explicitní let Rozsah

Pokud jsem vás přesvědčil, že implicitní povaha let deklarace by mohly být problém/nebezpečí -- pokud nejste extrémně opatrní, stejně jako každý jiný vývojář, který kdy pracuje na vašem kódu! -- jaká je tedy alternativa? Vyhneme se zcela blokování?

Ne! Existují lepší způsoby.

Za prvé, můžete se vnutit stylu/idiomu, který nejenže vloží váš let deklarace v horní části rozsahu, ale také to vytváří explicitní blok pro takový rozsah. Například:

if (a) {
    // more code

    // make an explicit scope block!
    { let b, c;
        // more code

        b = 10;

        // more code

        c = 1000;

        // more code

        if (b > 3) {
            // more code

            console.log( b + c );

            // more code
        }
    }

    // more code
}

Zde uvidíte, že jsem vytvořil nahý { .. } spárujte a vložte let b, c; prohlášení úplně nahoře, dokonce na stejném řádku. Uvádím co nejjasněji a nejjasněji, že se jedná o blok rozsahu a že obsahuje b a c .

Pokud později potřebuji přesunout nějaký b kód a jdu najít kombinovaný rozsah pro b a c , je nejen snazší rozpoznat, ale také snáze proveditelné, že mohu přesunout celý { let b, c; .. } bezpečně zablokovat.

Je to dokonalé? Samozřejmě že ne. Ale je to lepší a má menší rizika a menší mentální daň (i o trochu) než implicitní styl/idiomy z dřívější doby. Žádám vás všechny, jakmile začnete používat let rozsah bloku, zvažte prosím a preferujte explicitnější formulář přes implicitní formulář.

Vždy explicitní?

Ve skutečnosti bych řekl, že je explicitní je tak důležité, že jediná výjimka, kterou jsem z tohoto "pravidla" našel, je, že se mi líbí a používám for (let i=0; .. ) .. . Je diskutabilní, pokud je to implicitní nebo explicitní . Řekl bych, že je to více explicitní než implicitní . Ale možná to není tak úplně explicitní jako { let i; for (i=0; ..) .. } .

Existuje skutečně dobrý důvod, proč for (let i=0; ..) .. mohlo by to být lepší. Týká se to uzávěrů dalekohledu a je to velmi cool a výkonné!

{ let i;
    for (i=1; i<=5; i++) {
        setTimeout(function(){
            console.log("i:",i);
        },i*1000);
    }
}

Tento kód bude, stejně jako jeho typičtější var protějšek, nikoli práce , tím se vytiskne i: 6 pětkrát. Ale tento kód funguje :

for (let i=1; i<=5; i++) {
    setTimeout(function(){
        console.log("i:",i);
    },i*1000);
}

Vytiskne i: 1 , i: 2 , i: 3 , atd. Proč?

Protože specifikace ES6 ve skutečnosti říká, že let i v for rozsahy záhlaví smyčky i nejen na for smyčky, ale do každé iterace for smyčka . Jinými slovy, chová se takto:

{ let k;
    for (k=1; k<=5; k++) {
        let i = k; // <-- new `i` for each iteration!
        setTimeout(function(){
            console.log("i:",i);
        },i*1000);
    }
}

To je super – řeší to velmi běžný problém, který mají vývojáři s uzávěry a smyčkami!

Poznámka: To zatím nefunguje v prohlížečích, dokonce ani v těch s let . Specifikace ES6 to vyžaduje, ale v době psaní této zprávy žádný prohlížeč nevyhovuje této konkrétní per-iterační nuanci. Pokud chcete důkaz, zkuste vložit kód do ES6fiddle. Viz...

Ještě explicitnější let Rozsah

Dobře, možná jsem vás přesvědčil, že explicitní dalekohledy jsou o něco lepší. Nevýhodou výše uvedeného je, že není vynutitelně vyžadováno, abyste se řídili stylem/idiomem { let b, c; .. } , což znamená, že vy nebo někdo jiný ve vašem týmu to můžete pokazit a nesledovat to.

Je tu další možnost. Místo použití "let deklarační formulář", mohli bychom použít "let blokový formulář":

if (a) {
    // make an explicit scope block!
    let (b, c) {
        // ..
    }
}

Je to malá změna, ale podívejte se pozorně:let (b, c) { .. } vytvoří explicitní blok oboru pro b a c . Syntakticky vyžaduje b a c být deklarován nahoře a je to blok, který není nic jiného než rozsah.

Podle mého názoru je to nejlepší způsob použití let -založený blokový rozsah.

Ale je tu problém. Výbor TC39 hlasoval pro nezařazení tento konkrétní tvar let v ES6. Může přijít později nebo nikdy, ale rozhodně není v ES6.

Fuj. Ale toto není první ani poslední, kdy něco, co je výhodnější, prohrává ve prospěch horší možnosti.

Takže jsme uvízli v předchozí podobě?

Možná ne. Vytvořil jsem nástroj nazvaný "let-er", což je transpiler pro "let block form" kód. Ve výchozím nastavení je v režimu pouze ES6 a vyžaduje kód jako:

let (b, c) {
    ..
}

A produkuje:

{ let b, c;
    ..
}

To není tak hrozné, že? Je to vlastně docela jednoduchá transformace, abyste získali nestandardní "let blokový formulář" do standardního "let formulář prohlášení". Po spuštění let-er pro tuto transformaci pak můžete použít běžný transpiler ES6 k cílení na prostředí starší než ES6 (prohlížeče atd.).

Pokud chcete použít let-er samostatný bez dalších transpilerů, pouze pro let -založené na rozsahu bloků, můžete volitelně nastavit režim/příznak ES3 a místo toho vytvoří toto (sice hackerský odpad):

try{throw void 0}catch( b ){try{throw void 0}catch( c ){
    ..
}}

Jo, používá to málo známý fakt, že try..catch má v catch zabudovaný rozsah bloků klauzule.

Nikdo nechce psát tento kód a nikomu se nelíbí snížený výkon, který přináší. Ale mějte na paměti, že je to zkompilovaný kód a je určen pouze pro cílení na opravdu staré prohlížeče, jako je IE6. Pomalejší výkon je nešťastný (v mých testech asi o 10 %), ale váš kód už v IE6 běží dost pomalu/špatně, takže...

Každopádně dopis ve výchozím nastavení cílí na standardní ES6, a proto se dobře hraje s jinými nástroji ES6, jako jsou standardní transpilery.

Můžete se rozhodnout, zda raději vytvořit kód s let (b, c) { .. } styl nebo je { let b, c; .. } Dobře?

Používám let-er teď v mých projektech. Myslím, že je to lepší způsob. A doufám, že si možná v ES7 členové TC39 uvědomí, jak důležité je přidat "let block form“ do JS, takže nakonec let-er může jít pryč!

V každém případě explicitní rozsah bloků je lepší než implicitní . Zablokujte rozsah zodpovědně.

let Nahrazuje var ?

Někteří prominentní členové komunity JS a výboru TC39 rádi říkají:"let je nový var ." Ve skutečnosti někteří doslova navrhli (doufejme v žertu!?) jednoduše provést globální hledání-n-nahrazení var pro let .

Nedokážu vyjádřit, jak neuvěřitelně hloupá by ta rada byla.

Za prvé, nebezpečí, která jsme zmínili výše, by se ve vašem kódu objevila mnohem pravděpodobněji, protože je pravděpodobné, že váš kód není dokonale napsán s ohledem na var používání. Tento druh kódu je například extrémně běžný:

if ( .. ) {
    var foo = 42;
}
else {
    var foo = "Hello World";
}

Všichni se pravděpodobně shodneme, že by mělo byly napsány jako:

var foo;

if ( .. ) {
    foo = 42;
}
else {
    foo = "Hello World";
}

Ale zatím to tak napsané není. Nebo omylem děláte věci jako:

b = 1;

// ..

var b;

Nebo se omylem spoléháte na uzavření ve smyčkách, které není v rozsahu bloku:

for (var i=0; i<10; i++) {
    if (i == 2) {
        setTimeout(function(){
            if (i == 10) {
                console.log("Loop finished");
            }
        },100);
    }
}

Pokud tedy jen slepě nahradíte var s let ve stávajícím kódu je docela velká šance, že alespoň nějaké místo náhodou přestane fungovat. Všechny výše uvedené by selhaly, pokud by let nahrazeno var , bez dalších změn.

Pokud se chystáte dovybavit stávající kód pomocí určování rozsahu bloku, musíte postupovat případ od případu, pečlivě a musíte uvažovat a racionalizovat, zda je to místo, kde je určování rozsahu bloku vhodné nebo ne.

Určitě budou místa, kde je var byl stylisticky použit a nyní let je lepší. Pokuta. Stále se mi nelíbí implicitní použití, ale pokud je to váš šálek čaje, tak ano.

Ale budou také místa, kde si ve své analýze uvědomíte, že kód má strukturální problémy, kde let by bylo nepohodlnější nebo by vytvořilo více matoucí kód. Na těchto místech se můžete rozhodnout kód opravit, ale můžete se také docela rozumně rozhodnout opustit var sám.

Zde je to, co mi nejvíce vadí na "let je nový var ":předpokládá to, ať už to přiznají nebo ne, elitářský názor, že veškerý kód JS by měl být dokonalý a měl by se řídit správnými pravidly. Kdykoli vytáhnete ty dřívější případy, zastánci jednoduše vrátí úder, "no, ten kód už byl špatný." "

."

Tak určitě. Ale to je vedlejší, ne hlavní. Stejně nepřátelské je říkat:„používejte pouze let pokud je váš rozsah již dokonalý nebo jste připraveni jej přepsat tak, aby byl dokonalý a dokonalý.“

Ostatní zastánci se to pokusí zmírnit slovy:"No, stačí použít let." pro všechny nové kódy."

To je ekvivalentně elitářské, protože opět předpokládá, že jakmile se naučíte let a rozhodnete se jej použít, bude se od vás očekávat, že napíšete celý nový kód, aniž byste narazili na nějaké vzorce nebezpečí.

Vsadím se, že členové TC39 to dokážou. Jsou opravdu chytří a opravdu důvěrní s JS. Ale my ostatní takové štěstí nemáme.

let je nový doplněk k var

Čím rozumnější a realističtější pohled, ten, který beru, protože moje primární rozhraní s JS je prostřednictvím studentů/účastníků, se kterými mluvím, učím je a pracuji s nimi, je přijmout refaktorování a zlepšování kódu jako proces, nikoli událost.

Jistě, když se naučíte osvědčené postupy určování rozsahu, měli byste kód vylepšit pokaždé, když se ho dotknete, a jistě, váš nový kód by měl být o něco lepší než váš starší kód. Ale nestačí jen přepnout výhybku čtením knihy nebo příspěvku na blogu, a teď máte najednou všechno perfektní.

Místo toho si myslím, že byste měli přijmout oba nové let a starý var jako užitečné signály ve vašem kódu.

Použijte let v místech, o kterých víte, že potřebujete rozsah bloků, a konkrétně jste přemýšleli o těchto důsledcích. Nadále však používejte var pro proměnné, které buď nelze snadno blokovat, nebo které by neměly mít blokový rozsah. V kódu reálného světa budou místa, kde budou některé proměnné správně vymezeny pro celou funkci, a pro tyto proměnné var je lepší signál.

function foo() {
    var a = 10;

    if (a > 2) {
        let b = a * 3;
        console.log(b);
    }

    if (a > 5) {
        let c = a / 2;
        console.log(c);
    }

    console.log(a);
}

V tomto kódu let křičí na mě:"Hej, mám blokový rozsah!" Upoutá moji pozornost a věnuji jí tak větší péči. var jen říká:"Hej, jsem stejná stará proměnná s rozsahem funkcí, protože budu používána v mnoha rozsahech."

Co takhle říct let a = 10 na nejvyšší úrovni funkce? můžete udělejte to a bude to fungovat dobře.

Ale nemyslím si, že je to dobrý nápad. Proč?

Nejprve ztratíte/zhoršíte rozdíl v signálu mezi var a let . Nyní je to pouze pozice, která signalizuje rozdíl, spíše než syntaxe.

Za druhé, je to stále potenciální nebezpečí. Měli jste někdy v programu podivnou chybu a začali házet try..catch kolem věcí pokusit se na to přijít? Určitě ano.

Jejda:

function foo() {
    try {
        let a = 10;

        if (a > 2) {
            let b = a * 3;
            console.log(b);
        }
    }
    catch (err) {
        // ..
    }

    if (a > 5) {
        let c = a / 2;
        console.log(c);
    }

    console.log(a);
}

Block scoping je skvělý, ale není to žádná stříbrná kulka a nehodí se ke všemu. Jsou místa, kde je rozsah funkce var s, a skutečně "zvedání" chování, jsou docela užitečné. Toto nejsou ohavná selhání v jazyce, která by měla být odstraněna. Jsou to věci, které by měly být používány zodpovědně, stejně jako let .

Zde je lepší způsob, jak to říci:"let je nový rozsah bloku var ". Toto prohlášení zdůrazňuje, že let by měl nahradit var pouze když var už stylisticky signalizovalo určení rozsahu bloku. V opačném případě ponechte var sám. Stále dělá svou práci docela dobře!

Přehled

Rozsah bloku je skvělý a let nám to dává. Ale buďte explicitní, pokud jde o rozsahy bloků. Vyhněte se implicitnímu let deklarace rozházené.

let + var , nikoli s/var/let/ . Jen se zamračte a pak se ušklíbněte na dalšího člověka, který vám řekne:„let je nový var ."

let zlepšuje možnosti rozsahu v JS, nikoli nahrazuje. var je stále užitečný signál pro proměnné, které se používají v celé funkci. Mít obojí a používat obojí znamená, že záměr určování rozsahu je jasnější, aby bylo možné porozumět a udržovat a prosazovat . To je velká výhra!