analyse af JavaScript-eksemplerne i Gary Bernhardts “hvad” – tale

dette indlæg er en hyldest til Gary Bernhardts fantastiske “Hvad” – tale, hvor han påpeger særegenhederne ved nogle sprogkonstruktioner i Ruby og JavaScript. Hvis du ikke har set foredraget endnu, anbefaler jeg stærkt, at du tager dig tid og gør netop det! Det er kun omkring 4 minutter lang og meget underholdende, Jeg lover.

i sin tale viser Gary disse fire fragmenter af JavaScript-kode:

særegenheder i JavaScript

vi ser masser af parenteser, seler og plustegn. Her er hvad disse fragmenter vurderer til:

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

da jeg så disse eksempler for første gang, tænkte jeg: “Hold da op, det ser rodet ud!”Resultaterne kan virke inkonsekvente eller endda vilkårlige, men bær over med mig her. Alle disse eksempler er faktisk meget konsistente og ikke så dårlige, som de ser ud!

#Fragment #1: +

lad os starte med det første fragment:

 + // ""

som vi kan se, resulterer anvendelse af + – operatøren på to tomme arrays i en tom streng. Dette skyldes, at strengrepræsentationen af et array er strengrepræsentationen af alle dens elementer, sammenkædet sammen med kommaer:

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

et tomt array indeholder ingen elementer, så dets strengrepræsentation er en tom streng. Derfor er sammenkædningen af to tomme strenge bare en anden tom streng.

#Fragment #2: + {}

så langt, så godt. Lad os nu undersøge det andet fragment:

 + {}// ""

Bemærk, at fordi vi ikke har at gøre med to tal, udfører + – operatøren igen strengsammenkædning i stedet for tilføjelse af to numeriske værdier.

i det foregående afsnit har vi allerede set, at strengrepræsentationen af et tomt array er en tom streng. Strengrepræsentationen af det tomme objekt bogstavelig her er standardværdien "". Forberedelse af en tom streng ændrer ikke værdien, så "" er det endelige resultat.

i JavaScript kan objekter implementere en særlig metode kaldet toString(), som returnerer en brugerdefineret strengrepræsentation af det objekt, metoden kaldes. Vores tomme objekt bogstaveligt implementerer ikke en sådan metode, så vi falder tilbage til standardimplementeringen af Object prototypen.

#Fragment #3: {} +

jeg vil hævde, at resultaterne hidtil ikke har været for uventede. De har simpelthen fulgt reglerne for type tvang og standardstrengrepræsentationer i JavaScript.

dog {} + er hvor udviklere begynder at blive forvirrede:

{} + // 0

hvorfor ser vi 0 (tallet nul), hvis vi skriver ovenstående linje i en JavaScript-REPL som bro. ser-konsollen? Skulle resultatet ikke være en streng, ligesom + {} var?

før vi løser gåden, skal du overveje de tre forskellige måder, hvorpå + operatøren kan bruges:

// 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

i de to første tilfælde er + operatøren en binær operatør, fordi den har to operander (til venstre og til højre). I det tredje tilfælde er + operatøren en unary operatør, fordi den kun har en enkelt operand (til højre).

overvej også de to mulige betydninger af {} i JavaScript. Normalt skriver vi {} for at betyde et tomt objekt bogstaveligt, men hvis vi er i sætningsposition, angiver JavaScript-grammatikken {} for at betyde en tom blok. Følgende stykke kode definerer to tomme blokke, hvoraf ingen er et objekt bogstaveligt:

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

lad os se på vores fragment igen:

{} + 

Lad mig ændre hvidrummet lidt for at gøre det tydeligere, hvordan JavaScript-motoren ser koden:

{ // Empty block}+;

nu kan vi tydeligt se, hvad der sker her. Vi har en blokerklæring efterfulgt af en anden erklæring, der indeholder et unary + udtryk, der fungerer på et tomt array. Det efterfølgende semikolon indsættes automatisk i henhold til ASI ‘ s regler (automatisk semikolon indsættelse).

du kan nemt kontrollere, at +evaluerer til 0. Det tomme array har en tom streng som sin strengrepræsentation, som igen konverteres til tallet nul af operatøren +. Endelig rapporteres værdien af den sidste sætning (+, i dette tilfælde) af bro.ser-konsollen.

Alternativt kan du føje begge kodestykker til en JavaScript-parser som Esprima og sammenligne de resulterende abstrakte syntakstræer. Her er AST for + {}:

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

her er AST for {} + :

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

forvirringen stammer fra en nuance af JavaScript-grammatikken, der bruger seler både til objektlitteraler og blokke. I udsagnsposition starter en åbningsbøjle en blok, mens en åbningsbøjle i ekspressionsposition starter et objekt bogstaveligt.

#Fragment #4: {} + {}

endelig, lad os hurtigt tage et kig på vores sidste fragment {} + {}:

{} + {}// NaN

nå, at tilføje to objektlitteraler er bogstaveligt talt “ikke et tal” – men tilføjer vi to objektlitteraler her? Lad ikke seler narre dig igen! Dette er hvad der sker:

{ // Empty block}+{};

det er stort set den samme aftale som i det foregående eksempel. Vi anvender dog nu unary plus-operatøren på et tomt objekt bogstaveligt. Det er stort set det samme som at gøre Number({}), hvilket resulterer i NaN, fordi vores objekt bogstaveligt ikke kan konverteres til et tal.

hvis du vil have JavaScript-motoren til at analysere koden som to tomme objektlitteraler, skal du pakke den første (eller hele koden) inden for parentes. Du skal nu se det forventede resultat:

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

åbningsparentesen får parseren til at forsøge at genkende et udtryk, hvorfor det ikke behandler {} som en blok (hvilket ville være en erklæring).

#Resume

du skal nu se, hvorfor de fire kodefragmenter vurderer, hvordan de gør. Det er slet ikke vilkårligt eller tilfældigt; reglerne for type tvang anvendes nøjagtigt som beskrevet i specifikationen og sproggrammatikken.

bare husk, at hvis en åbningsbøjle er det første tegn, der vises i en erklæring, vil den blive fortolket som starten på en blok snarere end et objekt bogstaveligt.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.