a JavaScript példák elemzése Gary Bernhardt” Wat “Talk-jában

ez a bejegyzés tisztelgés Gary Bernhardt fantasztikus” Wat ” talk-jában, amelyben rámutat a Ruby és a JavaScript egyes nyelvi konstrukcióinak sajátosságaira. Ha még nem nézte meg a beszélgetést, erősen ajánlom, hogy szánjon rá időt, és pontosan ezt tegye! Csak körülbelül 4 perc hosszú és nagyon szórakoztató, ígérem.

előadásában Gary bemutatja a JavaScript kód négy töredékét:

a JavaScript sajátosságai

sok zárójelet, zárójelet és pluszjelet látunk. Itt van, amit ezek a töredékek értékelik:

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

amikor először láttam ezeket a példákat, azt gondoltam: “Wow, ez rendetlennek tűnik!”Az eredmények ellentmondásosnak vagy akár önkényesnek tűnhetnek,de itt viseljem el. Ezek a példák valójában nagyon következetesek, és nem olyan rosszak, mint amilyennek látszanak!

# töredék #1: +

kezdjük az első töredékkel:

 + // ""

mint láthatjuk, a + operátor két üres tömbre történő alkalmazása üres karakterláncot eredményez. A tömb karakterlánc-ábrázolása ugyanis az összes elemének karakterlánc-ábrázolása, vesszővel összefűzve:

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

az üres tömb nem tartalmaz elemeket, így a karakterlánc-ábrázolása üres karakterlánc. Ezért két üres karakterlánc összefűzése csak egy újabb üres karakterlánc.

# töredék #2: + {}

eddig minden rendben. Most vizsgáljuk meg a második töredéket:

 + {}// ""

ne feledje, hogy mivel nem két számmal foglalkozunk, a + operátor ismét végrehajtja a karakterlánc-összefűzést két numerikus érték hozzáadása helyett.

az előző szakaszban már láttuk, hogy egy üres tömb karakterlánc-ábrázolása üres karakterlánc. Az üres objektum literál karakterlánc-ábrázolása itt az alapértelmezett "" érték. Az üres karakterlánc előreírása nem változtatja meg az értéket, így "" a végeredmény.

a JavaScriptben az objektumok megvalósíthatnak egy toString() nevű speciális módszert, amely a metódust meghívó objektum egyéni karakterlánc-ábrázolását adja vissza. Az üres objektum literál nem valósít meg ilyen módszert, ezért visszatérünk a Object prototípus alapértelmezett megvalósításához.

# töredék #3: {} +

azt állítom, hogy eddig az eredmények nem voltak túl váratlanok. Egyszerűen követik a kényszerítés és az alapértelmezett karakterlánc-reprezentációk szabályait a JavaScript-ben.

azonban {} + ahol a fejlesztők kezdenek összezavarodni:

{} + // 0

miért látjuk 0 (a szám nulla), ha beírjuk a fenti sort egy JavaScript REPL, mint a böngésző konzol? Nem kellene az eredménynek karakterláncnak lennie, mint + {} volt?

mielőtt megoldanánk a rejtvényt, fontolja meg a + operátor három különböző módját:

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

az első két esetben a + operátor bináris operátor, mert két operandusa van (bal és jobb oldalon). A harmadik esetben a + operátor unáris operátor, mert csak egyetlen operandusa van (a jobb oldalon).

fontolja meg a {} két lehetséges jelentését is a JavaScript-ben. Általában {} – et írunk, hogy üres objektum literált jelentsünk, de ha utasításpozícióban vagyunk, a JavaScript nyelvtan meghatározza {} hogy üres blokkot jelentsen. A következő kóddarab két üres blokkot határoz meg, amelyek közül egyik sem objektum literál:

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

vessünk egy pillantást újra a töredékünkre:

{} + 

hadd változtassam meg egy kicsit a szóközt, hogy világosabbá tegyem, hogy a JavaScript motor hogyan látja a kódot:

{ // Empty block}+;

most már tisztán látjuk, mi történik itt. Van egy blokk utasításunk, amelyet egy másik utasítás követ, amely egy üres tömbön működő unary + kifejezést tartalmaz. A záró pontosvessző automatikusan beillesztésre kerül az ASI (automatikus pontosvessző beillesztése) szabályai szerint.

böngészőkonzoljában könnyen ellenőrizheti, hogy a +értéke 0 – ra változik-e. Az üres tömb karakterlánc-ábrázolásaként egy üres karakterlánc van, amelyet viszont a + operátor nullára konvertál. Végül az utolsó utasítás értékét (ebben az esetben+) a böngészőkonzol jelenti.

Alternatív megoldásként mindkét kódrészletet betáplálhatja egy JavaScript-elemzőbe, például az Esprima-ba, és összehasonlíthatja a kapott absztrakt szintaxisfákat. Itt van az AST + {}:

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

és itt van az AST {} + :

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

a zavart a JavaScript nyelvtan árnyalata okozza, amely zárójeleket használ mind az objektum literálokhoz, mind a blokkokhoz. Utasítás helyzetben egy nyitó zárójel blokkot indít, míg kifejezési helyzetben egy nyitó zárójel indítja az objektum literálját.

# töredék #4: {} + {}

végül vessünk egy pillantást az utolsó töredékünkre {} + {}:

{} + {}// NaN

nos, két objektum literál hozzáadása szó szerint “nem szám” — de itt két objektum literált adunk hozzá? Ne hagyja, hogy a fogszabályzó újra becsapjon! Ez történik:

{ // Empty block}+{};

nagyjából ugyanaz az üzlet, mint az előző példában. Most azonban az unary plus operátort alkalmazzuk egy üres objektum literálra. Ez alapvetően ugyanaz, mint a Number({}), ami NaN – ot eredményez, mert az objektum literálunk nem konvertálható számra.

ha azt szeretné, hogy a JavaScript motor két üres objektum literálként értelmezze a kódot, tekerje zárójelbe az elsőt (vagy a teljes kóddarabot). Most látnia kell a várt eredményt:

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

a nyitó zárójel arra készteti az elemzőt, hogy megpróbálja felismerni egy kifejezést, ezért nem kezeli a {} blokkot (ami állítás lenne).

#Összegzés

most látnod kell, hogy a négy kódtöredék miért értékeli úgy, ahogy. Ez egyáltalán nem önkényes vagy véletlenszerű; A típusú kényszerítés szabályait pontosan a specifikációban és a nyelvtanban lefektetett módon alkalmazzák.

csak ne feledje, hogy ha egy nyitó zárójel az első karakter, amely megjelenik egy utasításban, akkor azt egy blokk kezdeteként értelmezik, nem pedig objektum literálként.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.