analýza příkladů JavaScriptu v „Wat“ Talk Garyho Bernhardta

tento příspěvek je poctou fantastickému „Wat“ talk Garyho Bernhardta, ve kterém poukazuje na zvláštnosti některých jazykových konstrukcí v Ruby a JavaScriptu. Pokud jste ještě nesledovali rozhovor, důrazně doporučuji, abyste si udělali čas a udělali přesně to! Je to jen asi 4 minuty dlouhé a velmi zábavné, slibuji.

ve své přednášce Gary předvádí tyto čtyři fragmenty kódu JavaScript:

 zvláštnosti v JavaScriptu

vidíme spoustu závorek, závorek a znamének plus. Zde je to, co tyto fragmenty hodnotí:

  • + == ""
  • + {} == ""
  • {} + == 0
  • {} + {} == NaN

když jsem tyto příklady viděl poprvé, pomyslel jsem si: „Páni, to vypadá chaoticky!“Výsledky se mohou zdát nekonzistentní nebo dokonce svévolné, ale mějte se mnou tady. Všechny tyto příklady jsou ve skutečnosti velmi konzistentní a nejsou tak špatné, jak vypadají!

# Fragment #1: +

začněme prvním fragmentem:

 + // ""

jak vidíme, použití operátoru + na dvě prázdná pole vede k prázdnému řetězci. Je to proto, že reprezentace řetězce pole je reprezentace řetězce všech jeho prvků, zřetězené spolu s čárkami:

.toString()// "1,2,3".toString()// "1,2".toString()// "1".toString()// ""

prázdné pole neobsahuje žádné prvky, takže jeho reprezentace řetězce je prázdný řetězec. Zřetězení dvou prázdných řetězců je tedy jen dalším prázdným řetězcem.

# Fragment #2: + {}

zatím je to dobré. Podívejme se nyní na druhý fragment:

 + {}// ""

Všimněte si, že protože nemáme co do činění se dvěma čísly, operátor + opět provádí zřetězení řetězců namísto přidání dvou číselných hodnot.

v předchozí části jsme již viděli, že reprezentace řetězce prázdného pole je prázdný řetězec. Řetězcová reprezentace prázdného literálu objektu je zde výchozí hodnotou "". Předepnutím prázdného řetězce se hodnota nezmění, takže "" je konečný výsledek.

v JavaScriptu mohou objekty implementovat speciální metodu nazvanou toString(), která vrací vlastní reprezentaci řetězce objektu, na kterém je metoda vyvolána. Náš prázdný objektový doslov takovou metodu neimplementuje, takže se vracíme k výchozí implementaci prototypu Object.

# Fragment #3: {} +

řekl bych, že zatím nebyly výsledky příliš nečekané. Prostě se řídili pravidly donucování typu a výchozími reprezentacemi řetězců v JavaScriptu.

nicméně, {} + je místo, kde vývojáři začínají být zmateni:

{} + // 0

proč vidíme 0 (číslo nula), pokud zadáme výše uvedený řádek do JavaScriptu REPL jako konzole prohlížeče? Neměl by být výsledkem řetězec, stejně jako + {}?

než vyřešíme hádanku, zvažte tři různé způsoby použití operátoru + :

// 1) Addition of two numeric values2 + 2 == 4// 2) String concatenation of two values"2" + "2" == "22"// 3) Conversion of a value to a number+2 == 2+"2" == 2

v prvních dvou případech je operátor + binární operátor, protože má dva operandy (vlevo a vpravo). Ve třetím případě je operátor + operátorem unary, protože má pouze jeden operand (vpravo).

zvažte také dva možné významy {} v JavaScriptu. Obvykle píšeme {} znamená prázdný objekt doslov, ale pokud jsme v pozici příkazu, gramatika JavaScript určuje {} znamená prázdný blok. Následující část kódu definuje dva prázdné bloky, z nichž žádný není doslovný objekt:

{}// Empty block{ // Empty block}

podívejme se znovu na náš fragment:

{} + 

dovolte mi trochu změnit mezery, aby bylo jasnější, jak motor JavaScript vidí kód:

{ // Empty block}+;

teď můžeme jasně vidět, co se tady děje. Máme příkaz blok následovaný dalším příkazem, který obsahuje unary + výraz pracující na prázdném poli. Zadní středník se vkládá automaticky podle pravidel ASI (automatické vložení středníku).

v konzole prohlížeče můžete snadno ověřit, že + vyhodnotí hodnotu 0. Prázdné pole má jako reprezentaci řetězce prázdný řetězec,který je operátorem + převeden na číslo nula. Nakonec je hodnota posledního příkazu (v tomto případě+) hlášena konzolou prohlížeče.

Alternativně můžete oba úryvky kódu vložit do analyzátoru JavaScriptu, jako je Esprima, a porovnat výsledné abstraktní syntaxe stromů. Zde je AST pro + {}:

{ "type": "Program", "body": }, "right": { "type": "ObjectExpression", "properties": } } } ], "sourceType": "script"}

a tady je AST pro {} + :

{ "type": "Program", "body": }, { "type": "ExpressionStatement", "expression": { "type": "UnaryExpression", "operator": "+", "argument": { "type": "ArrayExpression", "elements": }, "prefix": true } } ], "sourceType": "script"}

zmatek pramení z nuance gramatiky JavaScript, která používá závorky jak pro literály objektů,tak pro bloky. V pozici příkazu zahajovací ortéza spustí blok, zatímco v pozici výrazu zahajovací ortéza spustí doslovný objekt.

# Fragment #4: {} + {}

nakonec se rychle podívejme na náš poslední fragment {} + {}:

{} + {}// NaN

no, přidání dvou objektových literálů je doslova “ ne číslo — – ale přidáváme zde dva objektové literály? Nedovolte, aby vás rovnátka znovu oklamala! To je to, co se děje:

{ // Empty block}+{};

je to skoro stejná dohoda jako v předchozím příkladu. Nyní však aplikujeme operátor unary plus na prázdný objekt. To je v podstatě stejné jako dělat Number({}), což má za následek NaN, protože náš objektový doslov nelze převést na číslo.

pokud chcete, aby stroj JavaScript analyzoval kód jako dva prázdné literály objektů, zabalte první (nebo celý kus kódu) do závorek. Nyní byste měli vidět očekávaný výsledek:

({}) + {}// ""({} + {})// ""

úvodní závorka způsobí, že se analyzátor pokusí rozpoznat výraz, a proto nepovažuje {} za blok (což by byl příkaz).

# shrnutí

nyní byste měli vidět, proč čtyři fragmenty kódu vyhodnocují způsob, jakým to dělají. Není to vůbec libovolné nebo náhodné; pravidla nátlaku typu jsou aplikována přesně tak, jak je stanoveno ve specifikaci a jazykové gramatice.

jen mějte na paměti, že pokud je úvodní ortéza prvním znakem, který se objeví v příkazu, bude interpretován jako začátek bloku spíše než doslovný objekt.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.