het analyseren van de JavaScript-voorbeelden in Gary Bernhardt ‘ s “wat” Talk

dit bericht is een eerbetoon aan Gary Bernhardt ‘ s fantastische “Wat” talk waarin hij wijst op de eigenaardigheden van sommige taalconstructies in Ruby en JavaScript. Als je het gesprek nog niet hebt gezien, raad ik je sterk aan om de tijd te nemen en precies dat te doen! Het is maar 4 minuten lang en zeer vermakelijk, dat beloof ik.

in zijn lezing toont Gary deze vier fragmenten van JavaScript-code:

eigenaardigheden in JavaScript

we zien veel haakjes, accolades en plusteken. Hier is wat deze fragmenten evalueren om:

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

toen ik deze voorbeelden voor het eerst zag, dacht ik: “Wow, dat ziet er rommelig uit!”De resultaten lijken misschien inconsistent of zelfs willekeurig, maar heb even geduld met mij. Al deze voorbeelden zijn eigenlijk heel consistent en niet zo slecht als ze eruit zien!

# Fragment #1: +

laten we beginnen met het eerste fragment:

 + // ""

zoals we kunnen zien, resulteert het toepassen van de + operator op twee lege arrays in een lege string. Dit komt omdat de tekenreeksrepresentatie van een array de tekenrepresentatie van alle elementen is, samengevoegd met komma ‘ s:

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

een lege array bevat geen elementen, dus de string representatie is een lege string. Daarom is de aaneenschakeling van twee lege strings gewoon een andere lege string.

# Fragment #2: + {}

tot nu toe gaat het goed. Laten we nu het tweede fragment onderzoeken:

 + {}// ""

merk op dat omdat we niet met twee getallen te maken hebben, de + operator opnieuw tekenreekscombinatie uitvoert in plaats van twee numerieke waarden optellen.

in de vorige sectie hebben we al gezien dat de string representatie van een lege array een lege string is. De tekenreeksrepresentatie van het lege object is hier de standaardwaarde "". Het toevoegen van een lege tekenreeks verandert de waarde niet, dus "" is het eindresultaat.

in JavaScript kunnen objecten een speciale methode implementeren genaamd toString() die een aangepaste tekenreeksweergave geeft van het object waarop de methode wordt aangeroepen. Onze lege object letterlijke implementeert een dergelijke methode niet, dus we vallen terug op de standaard implementatie van het Object prototype.

# Fragment #3: {} +

ik zou zeggen dat de resultaten tot nu toe niet al te onverwacht waren. Ze hebben gewoon het volgen van de regels van het type dwang en standaard string representaties in JavaScript.

echter, {} + is waar ontwikkelaars in de war raken:

{} + // 0

waarom zien we 0 (het getal nul) als we de bovenstaande regel typen in een JavaScript REPL zoals de browser console? Zou het resultaat geen tekenreeks moeten zijn, net zoals + {} was?

voordat we het raadsel oplossen, overweeg de drie verschillende manieren waarop de + operator kan worden gebruikt:

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

in de eerste twee gevallen is de + – operator een binaire operator omdat deze twee operanden heeft (links en rechts). In het derde geval is de + – operator een unary-operator omdat deze slechts één operand heeft (rechts).

overweeg ook de twee mogelijke betekenissen van {} in JavaScript. Meestal schrijven we {} om een leeg object letterlijk te betekenen, maar als we in statement positie zijn, specificeert de JavaScript grammatica {} om een leeg blok te betekenen. Het volgende stukje code definieert twee lege blokken, waarvan er geen een object letterlijk is:

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

laten we nog eens naar ons fragment kijken.:

{} + 

laat me de witruimte een beetje veranderen om het duidelijker te maken hoe de JavaScript-engine de code ziet:

{ // Empty block}+;

nu kunnen we duidelijk zien wat hier gebeurt. We hebben een blok statement gevolgd door een ander statement dat een unary + expressie bevat die werkt op een lege array. De puntkomma wordt automatisch ingevoegd volgens de regels van ASI (automatische puntkomma-invoeging).

u kunt eenvoudig in uw browserconsole controleren of + evalueert naar 0. De lege array heeft een lege string als string representatie, die op zijn beurt wordt geconverteerd naar het getal nul door de + operator. Ten slotte wordt de waarde van het laatste statement (+, in dit geval) gerapporteerd door de browserconsole.

als alternatief kunt u beide codefragmenten naar een JavaScript-parser zoals Esprima voeren en de resulterende abstracte syntaxisbomen vergelijken. Hier is de AST voor + {}:

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

en hier is de AST voor {} + :

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

de verwarring komt voort uit een nuance van de JavaScript grammatica die beugels gebruikt zowel voor object literals en blokken. In statement positie, een opening brace Start een blok, terwijl in expression positie een opening brace Start een object letterlijk.

# Fragment #4: {} + {}

tot slot, laten we snel een kijkje nemen op ons laatste fragment {} + {}:

{} + {}// NaN

nou, twee objectletters toevoegen is letterlijk ” geen getal — – maar voegen we hier twee objectletters toe? Laat je niet weer misleiden door de beugel! Dit is wat er gebeurt.:

{ // Empty block}+{};

het is ongeveer dezelfde deal als in het vorige voorbeeld. Echter, we zijn nu de unary plus operator toe te passen op een leeg object letterlijk. Dat is in principe hetzelfde als het doen van Number({}), wat resulteert in NaN omdat ons object letterlijk niet kan worden geconverteerd naar een getal.

als u wilt dat de JavaScript-engine de code ontleedt als twee lege objectletters, wikkel de eerste (of het hele stuk code) tussen haakjes. U ziet nu het verwachte resultaat:

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

het openingshaakje zorgt ervoor dat de parser een uitdrukking probeert te herkennen, daarom behandelt het {} niet als een blok (wat een statement zou zijn).

# Summary

u moet nu zien waarom de vier codefragmenten de manier waarop ze werken evalueren. Het is niet willekeurig of willekeurig helemaal; de regels van het type dwang worden toegepast precies zoals uiteengezet in de specificatie en de taal grammatica.

houd er rekening mee dat als een opening brace het eerste teken is dat in een statement verschijnt, het zal worden geïnterpreteerd als het begin van een blok in plaats van een object letterlijk.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.