UNPKG

wtfjs

Version:

A list of funny and tricky JavaScript examples

1,563 lines (1,087 loc) 58.7 kB
# What the f\*ck JavaScript? [![WTFPL 2.0][license-image]][license-url] [![NPM version][npm-image]][npm-url] > Una raccolta di snippet ingannevoli e divertenti scritti in JavaScript JavaScript è un ottimo linguaggio. Ha una sintassi semplice, un grande ecosistema e, quello che conta veramente, una community fantastica. Allo stesso tempo, sappiamo che JavaScript è un linguaggio abbastanza strano con delle parti cervellotiche. Alcune di queste possono rendere il nostro lavoro un inferno, alcune invece possono farci ridere a crepapelle. L'idea per WTFJS è di [Brian Leroux](https://twitter.com/brianleroux). Questo elenco è largamente ispirato al suo talk [**“WTFJS”** at dotJS 2012](https://www.youtube.com/watch?v=et8xNAc2ic8): [![dotJS 2012 - Brian Leroux - WTFJS](https://img.youtube.com/vi/et8xNAc2ic8/0.jpg)](https://www.youtube.com/watch?v=et8xNAc2ic8) # Node Packaged Manuscript Puoi installare questo manuale con `npm`. Lancia semplicemente: ``` $ npm install -g wtfjs ``` Ora dovresti essere in grado di eseguire `wtfjs` dalla riga di comando. Altrimenti puoi continuare tranquillamente a leggerlo qui. Il codice sorgente lo puoi trovare qui: <https://github.com/denysdovhan/wtfjs> # Traduzioni Attualmente **wtfjs** è disponibile nelle seguenti lingue: - [中文版](./README-zh-cn.md) - [Français](./README-fr-fr.md) - [Português do Brasil](./README-pt-br.md) - [Polski](./README-pl-pl.md) - [Italiano](./README-it-it.md) [**Richiedi un'altra traduzione**][tr-request] [tr-request]: https://github.com/denysdovhan/wtfjs/issues/new?title=Translation%20Request:%20%5BPlease%20enter%20language%20here%5D&body=I%20am%20able%20to%20translate%20this%20language%20%5Byes/no%5D <!-- prettier-ignore-start --> <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> # Table of Contents - [💪🏻 Motivazione](#-motivazione) - [✍🏻 Notazione](#-notazione) - [👀 Esempi](#-esempi) - [`[]` è uguale a `![]`](#-%C3%A8-uguale-a-) - [`true` è diverso da `![]`, ma anche diverso da `[]`](#true-%C3%A8-diverso-da--ma-anche-diverso-da-) - [true è false](#true-%C3%A8-false) - [baNaNa](#banana) - [`NaN` non è `NaN`](#nan-non-%C3%A8-nan) - [È un fail](#%C3%A8-un-fail) - [`[]` è truthy, ma non `true`](#-%C3%A8-truthy-ma-non-true) - [`null` è falsy, ma non `false`](#null-%C3%A8-falsy-ma-non-false) - [`document.all` è un object, ma è undefined](#documentall-%C3%A8-un-object-ma-%C3%A8-undefined) - [Il numero più piccolo rappresentabile è maggiore di zero](#il-numero-pi%C3%B9-piccolo-rappresentabile-%C3%A8-maggiore-di-zero) - [function non è una function](#function-non-%C3%A8-una-function) - [Sommare array](#sommare-array) - ["Trailing commas" in un array](#trailing-commas-in-un-array) - [L'operatore di uguaglianza sugli array è un mostro](#loperatore-di-uguaglianza-sugli-array-%C3%A8-un-mostro) - [`undefined` e `Number`](#undefined-e-number) - [`parseInt` è bast\*\*do](#parseint-%C3%A8-bast%5C%5Cdo) - [Math con `true` e `false`](#math-con-true-e-false) - [I commenti HTML sono validi anche in JavaScript](#i-commenti-html-sono-validi-anche-in-javascript) - [`NaN` è ~~not~~ a number](#nan-%C3%A8-not-a-number) - [`[]` e `null` sono objects](#-e-null-sono-objects) - [Incrementare numeri magicamente](#incrementare-numeri-magicamente) - [La precisione di `0.1 + 0.2`](#la-precisione-di-01--02) - [Patchare numeri](#patchare-numeri) - [Confrontare tre numeri](#confrontare-tre-numeri) - [Matematica spassosa](#matematica-spassosa) - [Somma di RegExps](#somma-di-regexps) - [Le stringhe non sono istanze di `String`](#le-stringhe-non-sono-istanze-di-string) - [Richiamare funzioni con le backticks](#richiamare-funzioni-con-le-backticks) - [Call call call](#call-call-call) - [Una proprietà chiamata `constructor`](#una-propriet%C3%A0-chiamata-constructor) - [Un Object usato come key nelle property di un oggetto](#un-object-usato-come-key-nelle-property-di-un-oggetto) - [Accedere ai prototypes con `__proto__`](#accedere-ai-prototypes-con-__proto__) - [`` `${{Object}}` ``](#-object-) - [Destructuring con valori di default](#destructuring-con-valori-di-default) - [Puntini e lo spreading](#puntini-e-lo-spreading) - [Labels](#labels) - [Labels annidate](#labels-annidate) - [Un `try..catch` insidioso](#un-trycatch-insidioso) - [Si tratta di ereditarietà multipla?](#si-tratta-di-ereditariet%C3%A0-multipla) - [Un generator che produce se stesso](#un-generator-che-produce-se-stesso) - [Una classe di tipo class](#una-classe-di-tipo-class) - [Oggetti non-coercible](#oggetti-non-coercible) - [Arrow functions strambe](#arrow-functions-strambe) - [Arrow functions non possono essere un costruttore](#arrow-functions-non-possono-essere-un-costruttore) - [`arguments` e arrow functions](#arguments-e-arrow-functions) - [Uno strano return](#uno-strano-return) - [Concatenare assegnamenti su un object](#concatenare-assegnamenti-su-un-object) - [Accedere alle properties di un object con gli array](#accedere-alle-properties-di-un-object-con-gli-array) - [Null e gli operatori relazionali](#null-e-gli-operatori-relazionali) - [`Number.toFixed()` mostra numeri diversi](#numbertofixed-mostra-numeri-diversi) - [`Math.max()` più piccolo di `Math.min()`](#mathmax-pi%C3%B9-piccolo-di-mathmin) - [Confrontare `null` con `0`](#confrontare-null-con-0) - [Alcune ridichiarazioni di variabili](#alcune-ridichiarazioni-di-variabili) - [Comportamento di default di Array.prototype.sort()](#comportamento-di-default-di-arrayprototypesort) - [resolve() non restituisce un'istanza di Promise](#resolve-non-restituisce-unistanza-di-promise) - [📚 Other resources](#-other-resources) - [🎓 License](#-license) <!-- END doctoc generated TOC please keep comment here to allow auto update --> <!-- prettier-ignore-end --> # 💪🏻 Motivazione > Just for fun > > &mdash; _[**“Just for Fun: The Story of an Accidental Revolutionary”**](https://en.wikipedia.org/wiki/Just_for_Fun), Linus Torvalds_ Lo scopo principale di questo elenco è quello di raccogliere alcuni esempi strambi e mostrarne il loro funzionamento, se possibile. Semplicemente per il fatto che è divertente imparare qualcosa che non sapevamo prima. Se sei un principiante puoi utilizzare questi appunti per approfondire JavaScript. Spero che questi appunti ti motivino a leggerne le specifiche. Se sei uno sviluppatore senior, considera questi esempi come un'ottimo punto di riferimento per tutte quelle stranezze e stramberie del tuo amato JavaScript. Ad ogni modo, leggilo. Probabilmente imparerai qualcosa di nuovo. # ✍🏻 Notazione **`// ->`** viene utilizzato per indicare il risultato di un'espressione. Ad esempio: ```js 1 + 1; // -> 2 ``` **`// >`** significa il risultato di `console.log` o di un altro output. Ad esempio: ```js console.log("hello, world!"); // > hello, world! ``` **`//`** è semplicemente un commento utilizzato per le spiegazioni. Esempio: ```js // Assegnare una funzione ad una costante const foo = function() {}; ``` # 👀 Esempi ## `[]` è uguale a `![]` Array è uguale a not array: ```js [] == ![]; // -> true ``` ### 💡 Spiegazione: L'opratore di abstract equality converte entrambi gli operandi prima di confrontarli, e diventano entrambi `0` per ragioni differenti. Gli Array sono truthy, quindi sulla destra, l'opposto di un valore truthy è `false`, che viene quindi forzato a diventare uno `0`. Sul lato sinistro però l'array vuoto viene forzato a diventare un numero senza prima essere convertito in un valore booleano, e gli array vuoti vengono forzati a `0` a prescindere che siano truthy. Qui possiamo vedere come viene semplificata l'espressione: ```js +[] == +![]; 0 == +false; 0 == 0; true; ``` Vedi anche [`[]` è truthy, ma non `true`](#-is-truthy-but-not-true). - [**12.5.9** Logical NOT Operator (`!`)](https://www.ecma-international.org/ecma-262/#sec-logical-not-operator) - [**7.2.13** Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## `true` è diverso da `![]`, ma anche diverso da `[]` Array è diverso da `true`, ma anche not Array è diverso da `true`; Array è uguale a `false`, ma anche not Array è uguale a `false`: ```js true == []; // -> false true == ![]; // -> false false == []; // -> true false == ![]; // -> true ``` ### 💡 Spiegazione: ```js true == []; // -> false true == ![]; // -> false // Secondo le specifiche true == []; // -> false toNumber(true); // -> 1 toNumber([]); // -> 0 1 == 0; // -> false true == ![]; // -> false ![]; // -> false true == false; // -> false ``` ```js false == []; // -> true false == ![]; // -> true // Secondo le specifiche false == []; // -> true toNumber(false); // -> 0 toNumber([]); // -> 0 0 == 0; // -> true false == ![]; // -> true ![]; // -> false false == false; // -> true ``` - [**7.2.13** Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## true è false ```js !!"false" == !!"true"; // -> true !!"false" === !!"true"; // -> true ``` ### 💡 Spiegazione: Considera questo, step-by-step: ```js // true è 'truthy' e rappresentato dal valore 1 (number), 'true' in formato stringa è NaN. true == "true"; // -> false false == "false"; // -> false // 'false' non è la stringa vuota, quindi è un valore truthy !!"false"; // -> true !!"true"; // -> true ``` - [**7.2.13** Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## baNaNa ```js "b" + "a" + +"a" + "a"; // -> 'baNaNa' ``` Questo è un giochino old-school in JavaScript, rivisitato. L'originale è questo: ```js "foo" + +"bar"; // -> 'fooNaN' ``` ### 💡 Spiegazione: L'espressione viene valutata come `'foo' + (+'bar')`, che converte `'bar'` in "not a number". - [**12.8.3** The Addition Operator (`+`)](https://www.ecma-international.org/ecma-262/#sec-addition-operator-plus) - [12.5.6 Unary + Operator](https://www.ecma-international.org/ecma-262/#sec-unary-plus-operator) ## `NaN` non è `NaN` ```js NaN === NaN; // -> false ``` ### 💡 Spiegazione: Le specifiche definiscono rigorosamente la logica dietro a questo comportamento: > 1. Se `Type(x)` è diverso da `Type(y)`, return **false**. > 2. Se `Type(x)` è Number, allora > 1. Se `x` è **NaN**, return **false**. > 2. Se `y` è **NaN**, return **false**. > 3. … … … > > &mdash; [**7.2.14** Strict Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-strict-equality-comparison) Seguendo la definizione di `NaN` da quella dell'IEEE: > Sono possibili quattro relazioni mutuamente esclusive: less than, equal, greater than, e unordered. L'ultimo caso si presenta quando almeno un operando è NaN. Tutt i NaN se comparati risulteranno unordered, inclusa la comparazione con se stesso. > > &mdash; [“What is the rationale for all comparisons returning false for IEEE754 NaN values?”](https://stackoverflow.com/questions/1565164/1573715#1573715) at StackOverflow ## È un fail Non crederai ai tuoi occhi, ma... ```js (![] + [])[+[]] + (![] + [])[+!+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]]; // -> 'fail' ``` ### 💡 Spiegazione: Rompendo quell'ammasso di simboli in pezzettini, possiamo notare che il seguente pattern si ripete spesso: ```js ![] + []; // -> 'false' ![]; // -> false ``` Quindi proviamo a sommare `[]` a `false`. Ma a causa di una serie di chiamate interne (`binary + Operator` -> `ToPrimitive` -> `[[DefaultValue]]`) otteniamo la conversione dell'operando a destra in una stringa: ```js ![] + [].toString(); // 'false' ``` Se pensiamo ad una stringa come un Array, possiamo accedere al suo primo elemento con `[0]`: ```js "false"[0]; // -> 'f' ``` Il resto è ovvio, ma la `i` è complicata. La `i` in `fail` viene ottenuta generando la stringa `'falseundefined'` e prendendo l'elemento all'indice `['10']` ## `[]` è truthy, ma non `true` Un array è un valore truthy, ma non è uguale a `true`. ```js !![] // -> true [] == true // -> false ``` ### 💡 Spiegazione: Ecco i link alle sezioni corrispondenti della specifica ECMA-262: - [**12.5.9** Logical NOT Operator (`!`)](https://www.ecma-international.org/ecma-262/#sec-logical-not-operator) - [**7.2.13** Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## `null` è falsy, ma non `false` Nonostante il fatto che `null` sia un valore falsy, non è uguale a `false`. ```js !!null; // -> false null == false; // -> false ``` Allo stesso modo, altri valori falsy, come `0` o `''` sono uguali a `false`. ```js 0 == false; // -> true "" == false; // -> true ``` ### 💡 Spiegazione: La spiegazione è la stessa dell'esempio precedente. Ecco il link corrispondente: - [**7.2.13** Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## `document.all` è un object, ma è undefined > ⚠️ Questo fa parte delle Browser API e non funziona su Node.js ⚠️ Nonostante il fatto che `document.all` sia un oggetto array-like e permette l'accesso al DOM della pagina, risponde alla funzione `typeof` con `undefined`. ```js document.all instanceof Object; // -> true typeof document.all; // -> 'undefined' ``` Allo stesso modo, `document.all` è diverso da `undefined`. ```js document.all === undefined; // -> false document.all === null; // -> false ``` Ma contemporaneamente: ```js document.all == null; // -> true ``` ### 💡 Spiegazione: > `document.all` veniva utilizzato per accedere agli elementi del DOM, nelle vecchie versioni di IE. Nonostante non sia mai diventato uno standard, veniva ampiamente utilizzato in codice JS non proprio recentissimo. Quando vennero rilasciate le nuove APIs (come `document.getElementById`) questa API divenne obsoleta e il comitato dello standard dovette decidere cosa farne. A causa del suo uso spropositato l'API venne mantenuta ma venne introdotta una violazione intenzionale nelle speficiche di JavaScript. > Il motivo per il quale risponde a `false` quando si utilizza l'operatore di [Strict Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-strict-equality-comparison) con `undefined`, mentre `true` quando si utilizza l'operatore di [Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) è a causa della violazione intenzionale inserita nella specifica che la permette in modo esplicito. > &mdash; [“Obsolete features - document.all”](https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-all) at WhatWG - HTML spec > &mdash; [“Chapter 4 - ToBoolean - Falsy values”](https://github.com/getify/You-Dont-Know-JS/blob/0d79079b61dad953bbfde817a5893a49f7e889fb/types%20%26%20grammar/ch4.md#falsy-objects) at YDKJS - Types & Grammar ## Il numero più piccolo rappresentabile è maggiore di zero `Number.MIN_VALUE` è il numero più piccolo rappresentabile, che è maggiore di zero: ```js Number.MIN_VALUE > 0; // -> true ``` ### 💡 Spiegazione: > `Number.MIN_VALUE` è `5e-324`, ovvero il più piccolo numero positivo che può essere rappresentato con precisione float, cioè quello che si può ottenere il più vicino possibile allo zero. Definisce la migliore risoluzione che un tipo di dato float può fornire. > > Il numero più piccolo in assoluto è `Number.NEGATIVE_INFINITY` nonostante non sia effettivamente un tipo numerico. > > &mdash; [“Why is `0` less than `Number.MIN_VALUE` in JavaScript?”](https://stackoverflow.com/questions/26614728/why-is-0-less-than-number-min-value-in-javascript) at StackOverflow - [**20.1.2.9** Number.MIN_VALUE](https://www.ecma-international.org/ecma-262/#sec-number.min_value) ## function non è una function > ⚠️ Un bug presente in V8 v5.5 o inferiore (Node.js <=7) ⚠️ Tutti conoscerete la noiosa _undefined is not a function_, ma questa? ```js // Dichiara una classe che estende null class Foo extends null {} // -> [Function: Foo] new Foo() instanceof null; // > TypeError: function is not a function // > at … … … ``` ### 💡 Spiegazione: Questo non è parte delle specifiche. È semplicemente un bug che ora è stato risolto, quindi non dovrebbero esserci problemi con questo in futuro. ## Sommare array E se provassimo a sommare due array? ```js [1, 2, 3] + [4, 5, 6]; // -> '1,2,34,5,6' ``` ### 💡 Spiegazione: Viene svolta la concatenazione. il procedimento step-by-step è il seguente: ```js [1, 2, 3] + [4, 5, 6][ // chiama toString() (1, 2, 3) ].toString() + [4, 5, 6].toString(); // concatenazione "1,2,3" + "4,5,6"; // -> ("1,2,34,5,6"); ``` ## "Trailing commas" in un array Creiamo un array con 4 elementi vuoti. Nonostante ciò, si ottiene un array con 3 elementi, a causa delle "trailing commas": ```js let a = [, , ,]; a.length; // -> 3 a.toString(); // -> ',,' ``` ### 💡 Spiegazione: > **Trailing commas** (anche chiamate "final commas") sono utili quando si aggiungono nuovi elementi, parametri o proprietà in codice JavaScript. Se si vuole aggiungere una nuova proprietà si può semplicemente aggiungere una nuova riga senza modificare quella precedente, se quella linea presenta già una virgola alla fine. Questo rende i diffs dei sistemi di version-control più puliti e modificare il codice è leggermente meno problematico. > > &mdash; [Trailing commas](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Trailing_commas) at MDN ## L'operatore di uguaglianza sugli array è un mostro L'operatore di uguaglianza sugli array in JS è un mostro, come possiamo osservare sotto: ```js [] == '' // -> true [] == 0 // -> true [''] == '' // -> true [0] == 0 // -> true [0] == '' // -> false [''] == 0 // -> true [null] == '' // true [null] == 0 // true [undefined] == '' // true [undefined] == 0 // true [[]] == 0 // true [[]] == '' // true [[[[[[]]]]]] == '' // true [[[[[[]]]]]] == 0 // true [[[[[[ null ]]]]]] == 0 // true [[[[[[ null ]]]]]] == '' // true [[[[[[ undefined ]]]]]] == 0 // true [[[[[[ undefined ]]]]]] == '' // true ``` ### 💡 Spiegazione: Guarda attentamente gli esempi precedenti! Il comportamento viene spiegato nella sezione [**7.2.13** Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) delle specifiche. ## `undefined` e `Number` Se non passiamo argomenti al costruttore di `Number`, otteniamo `0`. Il valore `undefined` viene assegnato di default quando non viene passato alcun valore, quindi possiamo aspettarci che `Number` senza parametri prenda `undefined` come valore del suo parametro. Invece quando inseriamo `undefined`, otteniamo `NaN`. ```js Number(); // -> 0 Number(undefined); // -> NaN ``` ### 💡 Spiegazione: In base alle specifiche: 1. Se non viene passato alcun parametro durante l'invocazione della funzione, `n` viene valorizzato a `+0`. 2. Altrimenti, `n` sarà il risultato di `ToNumber(value)`. 3. Nel caso di `undefined`, `ToNumber(undefined)` deve restituire `NaN`. Qui la sezione corrispondente: - [**20.1.1** The Number Constructor](https://www.ecma-international.org/ecma-262/#sec-number-constructor) - [**7.1.3** ToNumber(`argument`)](https://www.ecma-international.org/ecma-262/#sec-tonumber) ## `parseInt` è bast\*\*do `parseInt` è famoso per le sue stranezze: ```js parseInt("f*ck"); // -> NaN parseInt("f*ck", 16); // -> 15 ``` **💡 Spiegazione:** Questo avviene perchè `parseInt` continuerà a svolgere il parsing carattere per carattere fino a che non trova un carattere che non riconosce. La `f` in `'f*ck'` è la rappresentazione esadecimale di `15`. Svolgere il parsing di `Infinity` a integer è qualcosa di... ```js // parseInt("Infinity", 10); // -> NaN // ... parseInt("Infinity", 18); // -> NaN... parseInt("Infinity", 19); // -> 18 // ... parseInt("Infinity", 23); // -> 18... parseInt("Infinity", 24); // -> 151176378 // ... parseInt("Infinity", 29); // -> 385849803 parseInt("Infinity", 30); // -> 13693557269 // ... parseInt("Infinity", 34); // -> 28872273981 parseInt("Infinity", 35); // -> 1201203301724 parseInt("Infinity", 36); // -> 1461559270678... parseInt("Infinity", 37); // -> NaN ``` Attenzione anche quando si svolge il parsing di `null`: ```js parseInt(null, 24); // -> 23 ``` **💡 Spiegazione:** > Si sta convertendo `null` alla stringa `"null"` e provando poi a convertirla a sua volta. Per le radici da 0 a 23, non ci sono numerali per svolgere la conversione, quindi viene restituito NaN. A 24, `"n"`, la 14-esima lettera, viene aggiunta al sistema di numerazione. A 31, `"u"`, la 21-esima lettera, viene aggiunta e l'intera stringa può essere decodificata. A 37 non c'è più un valido insieme di numerazione che si può generare quindi viene restituito `NaN`. > > &mdash; [“parseInt(null, 24) === 23… wait, what?”](https://stackoverflow.com/questions/6459758/parseintnull-24-23-wait-what) at StackOverflow Non dimentichiamoci del sistema di numerazione ottale: ```js parseInt("06"); // 6 parseInt("08"); // 8 se è presente il supporto a ECMAScript 5 parseInt("08"); // 0 se assente il supporto a ECMAScript 5 ``` **💡 Spiegazione:** Se la stringa in input inizia con "0", la radice è 8 (octal) o 10 (decimal). Quale radice viene scelta dipende dall'implementazione. ECMAScript 5 specifica l'utilizzo di 10 (decimal), Ma non è ancora supportata da tutti i browser. Per questo motivo è sempre meglio specificare una radice quando si utilizza `parseInt`. `parseInt` converte sempre l'input in stringa: ```js parseInt({ toString: () => 2, valueOf: () => 1 }); // -> 2 Number({ toString: () => 2, valueOf: () => 1 }); // -> 1 ``` Attenzione quando si svolge il parsin di valori in virgola mobile: ```js parseInt(0.000001); // -> 0 parseInt(0.0000001); // -> 1 parseInt(1 / 1999999); // -> 5 ``` **💡 Spiegazione:** `ParseInt` prende una stringa come argomento e restituisce un intero in base alla radice specificata. `ParseInt` inoltre elimina tutto ciò che viene dopo e incluso il primo carattere non numerico nella stringa passata come parametro. `0.000001` Viene convertito nella stringa `"0.000001"` e `parseInt` restituisce `0`. Quando `0.0000001` viene convertito in stringa viene interpretato come `"1e-7"` e quindi `parseInt` restituisce `1`. `1/1999999` viene interpretato come `5.00000250000125e-7` e `parseInt` restituisce `5`. ## Math con `true` e `false` Facciamo un po' di calcoli: ```js true - true + // -> 2 (true + true) * (true + true) - true; // -> 3 ``` Hmmm... 🤔 ### 💡 Spiegazione: Possiamo forzare dei valori a numeri utilizzando il costruttore di `Number`. È abbastanza ovvio che `true` venga forzato a `1`: ```js Number(true); // -> 1 ``` L'operatore unario `+` prova a convertire il suo valore in un numero. Può convertire la rappresentazione testuale di interie e float, così come i valori non testuali `true`, `false`, e `null`. Se non riesce a svolgere il parsing di un particolare valore, restuituirà `NaN`. Questo significa che possiamo forzare facilmente `true` a `1`: ```js +true; // -> 1 ``` Quando svolgiamo addizioni o moltiplicazioni, viene invocato il metodo `ToNumber`. In base alla specifica questo metodo restituisce: > Se `parametro` è **true**, restituisci **1**. Se `parametro` è **false**, restituisci **+0**. È questo il motivo per il quale possiamo sommare valori booleani e ottenere risultati corretti. Sezioni corrispondenti: - [**12.5.6** Unary `+` Operator](https://www.ecma-international.org/ecma-262/#sec-unary-plus-operator) - [**12.8.3** The Addition Operator (`+`)](https://www.ecma-international.org/ecma-262/#sec-addition-operator-plus) - [**7.1.3** ToNumber(`argument`)](https://www.ecma-international.org/ecma-262/#sec-tonumber) ## I commenti HTML sono validi anche in JavaScript Non ci crederai, ma `<!--` (ovvero un commento in HTML) è un commento valido in JavaScript. ```js // commento valido <!-- anche questo ``` ### 💡 Spiegazione: Stupito? Commenti HTML-like sono stati pensati per permettere ai browser che non capivano il tag `<script>` di degradare in modo soft. Questi browser, ad esempio Netscape 1.x non sono più diffusi. Quindi non c'è proprio più alcun motivo per inserire commenti HTML nei tag `script` Dato che Node.js è basato sull'engine V8, i commenti HTML-like sono supportati anche dal runtime di the Node.js. Inoltre sono parte delle specifiche: - [**B.1.3** HTML-like Comments](https://www.ecma-international.org/ecma-262/#sec-html-like-comments) ## `NaN` è ~~not~~ a number Il tipo di `NaN` è `'number'`: ```js typeof NaN; // -> 'number' ``` ### 💡 Spiegazione: Spiegazione di come funzionano gli operatori `typeof` e `instanceof`: - [**12.5.5** The `typeof` Operator](https://www.ecma-international.org/ecma-262/#sec-typeof-operator) - [**12.10.4** Runtime Semantics: InstanceofOperator(`O`,`C`)](https://www.ecma-international.org/ecma-262/#sec-instanceofoperator) ## `[]` e `null` sono objects ```js typeof []; // -> 'object' typeof null; // -> 'object' // però null instanceof Object; // false ``` ### 💡 Spiegazione: Il comportamento dell'operatore `typeof` è definito nella seguente sezione delle specifiche: - [**12.5.5** The `typeof` Operator](https://www.ecma-international.org/ecma-262/#sec-typeof-operator) Secondo le specifiche, l'operatore `typeof` restituisce una stringa in base alla [Table 35: `typeof` Operator Results](https://www.ecma-international.org/ecma-262/#table-35). Per `null`, gli oggetti ordinari, esotici standard e non standard che non implementano `[[Call]]`, restituisce la stringa `"object"`. Comunque si può anche controllare il tipo di un oggetto utilizzando il metodo `toString`. ```js Object.prototype.toString.call([]); // -> '[object Array]' Object.prototype.toString.call(new Date()); // -> '[object Date]' Object.prototype.toString.call(null); // -> '[object Null]' ``` ## Incrementare numeri magicamente ```js 999999999999999; // -> 999999999999999 9999999999999999; // -> 10000000000000000 10000000000000000; // -> 10000000000000000 10000000000000000 + 1; // -> 10000000000000000 10000000000000000 + 1.1; // -> 10000000000000002 ``` ### 💡 Spiegazione: Questo è causato dallo standard IEEE 754-2008 per l'aritmetica binaria dei numeri in virgola mobile. A questa grandezze numeriche, arrotonda al numero pari più vicino. Leggi di più qui: - [**6.1.6** The Number Type](https://www.ecma-international.org/ecma-262/#sec-ecmascript-language-types-number-type) - [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) on Wikipedia ## La precisione di `0.1 + 0.2` Un giochino ben noto. La somma di `0.1` e `0.2` è completamente sbagliata: ```js 0.1 + 0.2( // -> 0.30000000000000004 0.1 + 0.2 ) === 0.3; // -> false ``` ### 💡 Spiegazione: La risposta alla domanda [”La matematica in virgola mobile è completamente rotta? ”](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) su StackOverflow: > Le costanti `0.2` e `0.3` nel programma saranno approssimazioni del loro vero valore. Il valore `double` più vicino a `0.2` è più grande del numero razionale `0.2` ma il `double` più vicino a `0.3` è più piccolo del numero razionale `0.3`. La somma di `0.1` e `0.2` risulta essere più grande del numero razionale `0.3` e quindi risultando diverso dalla costante presente nel codice. Questo problema è talmente noto che esiste anche il sito web [0.30000000000000004.com](http://0.30000000000000004.com/). Capita in tutti i linguaggi di programmazione che svolgono calcoli in virgola mobile, non solo JavaScript. ## Patchare numeri Possiamo aggiungere metodi nostri agli oggetti wrapper come `Number` o `String`. ```js Number.prototype.isOne = function() { return Number(this) === 1; }; (1.0).isOne(); // -> true (1).isOne(); // -> true (2.0) .isOne()( // -> false 7 ) .isOne(); // -> false ``` ### 💡 Spiegazione: Ovviamente possiamo estendere l'oggetto `Number` così come ogni altro oggetto in JavaScript. Non è comunque una pratica consigliata se il metodo definito non è parte delle specifiche. Ecco la lista delle proprietà dell'oggetto `Number`: - [**20.1** Number Objects](https://www.ecma-international.org/ecma-262/#sec-number-objects) ## Confrontare tre numeri ```js 1 < 2 < 3; // -> true 3 > 2 > 1; // -> false ``` ### 💡 Spiegazione: Perchè funziona in questo modo? Beh, il problema è nella prima parte dell'espressione. Ecco come funziona: ```js 1 < 2 < 3; // 1 < 2 -> true true < 3; // true -> 1 1 < 3; // -> true 3 > 2 > 1; // 3 > 2 -> true true > 1; // true -> 1 1 > 1; // -> false ``` Possiamo correggerlo con l'operatore _Greater than or equal (`>=`)_: ```js 3 > 2 >= 1; // true ``` Leggi più a riguardo degli operatori relazionali nelle specifiche: - [**12.10** Relational Operators](https://www.ecma-international.org/ecma-262/#sec-relational-operators) ## Matematica spassosa Spesso il risultato delle operazioni aritmetiche in JavaScript risulta essere abbastanza strano. Consideriamo questi esempi: ```js 3 - 1 // -> 2 3 + 1 // -> 4 '3' - 1 // -> 2 '3' + 1 // -> '31' '' + '' // -> '' [] + [] // -> '' {} + [] // -> 0 [] + {} // -> '[object Object]' {} + {} // -> '[object Object][object Object]' '222' - -'111' // -> 333 [4] * [4] // -> 16 [] * [] // -> 0 [4, 4] * [4, 4] // NaN ``` ### 💡 Spiegazione: Cosa succede nei primi quattro esempi? Ecco una piccola tabella per comprendere la somma in JavaScript: ``` Number + Number -> addition Boolean + Number -> addition Boolean + Boolean -> addition Number + String -> concatenation String + Boolean -> concatenation String + String -> concatenation ``` E per quanto riguarda gli altri esempi? I metodi `ToPrimitive` e `ToString` vengono chiamati implicitamente per `[]` e `{}` prima della somma. Leggi di più riguardo a questo processo nelle specifiche: - [**12.8.3** The Addition Operator (`+`)](https://www.ecma-international.org/ecma-262/#sec-addition-operator-plus) - [**7.1.1** ToPrimitive(`input` [,`PreferredType`])](https://www.ecma-international.org/ecma-262/#sec-toprimitive) - [**7.1.12** ToString(`argument`)](https://www.ecma-international.org/ecma-262/#sec-tostring) In particolare l'eccezione è in `{} + []`. Il motivo per cui differisce da `[] + {}` è che, senza parentesi, viene interpretato come un blocco di codice seguito dall'operatore unario +, convertendo `[]` in un numero. Come viene spiegato di seguito: ```js { // qui un blocco di codice } +[]; // -> 0 ``` Per ottenere lo stesso risultato di `[] + {}` possiamo racchiuderlo tra parentesi. ```js ({} + []); // -> [object Object] ``` ## Somma di RegExps Sapevi che si possono sommare numero in questo modo? ```js // Patch a toString method RegExp.prototype.toString = function() { return this.source; } / 7 / -/5/; // -> 2 ``` ### 💡 Spiegazione: - [**21.2.5.10** get RegExp.prototype.source](https://www.ecma-international.org/ecma-262/#sec-get-regexp.prototype.source) ## Le stringhe non sono istanze di `String` ```js "str"; // -> 'str' typeof "str"; // -> 'string' "str" instanceof String; // -> false ``` ### 💡 Spiegazione: Il costruttore di `String` restituisce una stringa: ```js typeof String("str"); // -> 'string' String("str"); // -> 'str' String("str") == "str"; // -> true ``` Proviamo con `new`: ```js new String("str") == "str"; // -> true typeof new String("str"); // -> 'object' ``` Object? eh? ```js new String("str"); // -> [String: 'str'] ``` Più informazioni sul costruttore di String nelle specifiche: - [**21.1.1** The String Constructor](https://www.ecma-international.org/ecma-262/#sec-string-constructor) ## Richiamare funzioni con le backticks Dichiariamo una funzione che logga tutti i parametri nella console: ```js function f(...args) { return args; } ``` Senza dubbio, saprai che possiamo richiamarla nel modo seguente: ```js f(1, 2, 3); // -> [ 1, 2, 3 ] ``` Ma sapevi di poter chiamare qualsiasi funzione con le backticks? ```js f`true is ${true}, false is ${false}, array is ${[1, 2, 3]}`; // -> [ [ 'true is ', ', false is ', ', array is ', '' ], // -> true, // -> false, // -> [ 1, 2, 3 ] ] ``` ### 💡 Spiegazione: Beh, questa non è per niente magia se hai familiarità con i _Tagged template literals_. Nell'esempio precedente, la funzione f `f` è un tag per i template literal. I tag prima dei template literals permettono di svolgere il parsing dei template con una funzione. Il primo parametro di una "funzione tag" contiene un array di stringhe. I parametri restanti sono relativi alle espressioni. Ad esempio: ```js function template(strings, ...keys) { // fai qualcosa con strings and keys… } ``` Questa è la [magia dietro](http://mxstbr.blog/2016/11/styled-components-magic-explained/) famosa libreria chiamata [💅 styled-components](https://www.styled-components.com/), molto popolare tra la community di React. Link alle specifiche: - [**12.3.7** Tagged Templates](https://www.ecma-international.org/ecma-262/#sec-tagged-templates) ## Call call call > Trovato da [@cramforce](http://twitter.com/cramforce) ```js console.log.call.call.call.call.call.apply(a => a, [1, 2]); ``` ### 💡 Spiegazione: Attenzione, ti può mettere in crisi il cervello! Prova ad eseguire questo codice a mente: stiamo applicando il metodo `call` usando il metodo `apply`. Leggi di più: - [**19.2.3.3** Function.prototype.call(`thisArg`, ...`args`)](https://www.ecma-international.org/ecma-262/#sec-function.prototype.call) - [**19.2.3.1 ** Function.prototype.apply(`thisArg`, `argArray`)](https://www.ecma-international.org/ecma-262/#sec-function.prototype.apply) ## Una proprietà chiamata `constructor` ```js const c = "constructor"; c[c][c]('console.log("WTF?")')(); // > WTF? ``` ### 💡 Spiegazione: Consideriamo questo esempio passo passo: ```js // Dichiariamo una costante che è la stringa 'constructor' const c = "constructor"; // c è una stringa c; // -> 'constructor' // Otteniamo il costruttore di string c[c]; // -> [Function: String] // Otteniamo il costruttore di constructor c[c][c]; // -> [Function: Function] // chiamiamo la funzione costruttore e gli passiamo // il corpo di una nuova funzione come parametro c[c][c]('console.log("WTF?")'); // -> [Function: anonymous] // Chiamiamo la funzione anonima risultante // Il risultato è loggare sulla console la stringa 'WTF?' c[c][c]('console.log("WTF?")')(); // > WTF? ``` `Object.prototype.constructor` restituisce un riferimento al costruttore di `Object` che ha creato l'oggetto. Con le stringhe è `String`, nel caso dei numeri è `Number` e così via. - [`Object.prototype.constructor`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) at MDN - [**19.1.3.1** Object.prototype.constructor](https://www.ecma-international.org/ecma-262/#sec-object.prototype.constructor) ## Un Object usato come key nelle property di un oggetto ```js { [{}]: {} } // -> { '[object Object]': {} } ``` ### 💡 Spiegazione: Perchè funziona così? Qui stiamo utilizzando le _Computed property name_. Quando passiamo un oggetto tra parentesi quadre, forza la conversione di quell'oggetto a stringa, quindi otteniamo la proprietà `'[object Object]'` e il valore `{}`. Possiamo realizzare un "brackets hell" in questo modo: ```js ({ [{}]: { [{}]: {} } }[{}][{}]); // -> {} // structure: // { // '[object Object]': { // '[object Object]': {} // } // } ``` Leggi di più a riguardo degli object literals qui: - [Object initializer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer) at MDN - [**12.2.6** Object Initializer](http://www.ecma-international.org/ecma-262/6.0/#sec-object-initializer) ## Accedere ai prototypes con `__proto__` Come sappiamo, i tipi primitivi non hanno prototipi. Però, se proviamo ad ottenere il valore di `__proto__` per i tipi primitivi, otteniamo questo: ```js (1).__proto__.__proto__.__proto__; // -> null ``` ### 💡 Spiegazione: Questo accade perchè quando qualcosa non ha un prototype, verrà inserito in un oggetto wrapper con un metodo `ToObject`. Quindi, passo passo: ```js (1) .__proto__( // -> [Number: 0] 1 ) .__proto__.__proto__( // -> {} 1 ).__proto__.__proto__.__proto__; // -> null ``` Qui più informazioni riguardo a `__proto__`: - [**B.2.2.1** Object.prototype.**proto**](https://www.ecma-international.org/ecma-262/#sec-object.prototype.__proto__) - [**7.1.13** ToObject(`argument`)](https://www.ecma-international.org/ecma-262/#sec-toobject) ## `` `${{Object}}` `` Quale è il risultato dell'espressione qui sotto? ```js `${{ Object }}`; ``` La risposta è: ```js // -> '[object Object]' ``` ### 💡 Spiegazione: Abbiamo definito un oggetto con una proprietà `Object` usando la _Shorthand property notation_: ```js { Object: Object; } ``` Quindi abbiamo passato questo oggetto al template literal, seguirà la chiamata al metodo `toString` per quell'oggetto. Ecco perchè otteniamo la stringa `'[object Object]'`. - [**12.2.9** Template Literals](https://www.ecma-international.org/ecma-262/#sec-template-literals) - [Object initializer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer) at MDN ## Destructuring con valori di default Considera l'esempio seguente: ```js let x, { x: y = 1 } = { x }; y; ``` L'esempio precedente è un ottima domanda per un colloquio di lavoro. Quale è il valore di `y`? La risposta è: ```js // -> 1 ``` ### 💡 Spiegazione: ```js let x, { x: y = 1 } = { x }; y; // ↑ ↑ ↑ ↑ // 1 3 2 4 ``` Con l'esempio precedente: 1. Dichiariamo `x` senza alcun valore, quindi risulta `undefined`. 2. Quindi inseriamo il valore di `x` all'interno della proprietà `x` dell'oggetto. 3. Quindi estraiamo il valore di `x` usando il destructuring e lo assegniamo a `y`. Se il valore non è definito, allora utilizziamo `1` come valore di default. 4. Restituiamo il valore di `y`. - [Object initializer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer) su MDN ## Puntini e lo spreading Si possono realizzare esempi interessanti utilizzando l'operatore di spreading e gli array. Considera questo: ```js [...[..."..."]].length; // -> 3 ``` ### 💡 Spiegazione: Perchè `3`? Quando utilizziamo [l'operatore di spread](http://www.ecma-international.org/ecma-262/6.0/#sec-array-initializer), viene chiamato il metodo `@@iterator`, e l'iteratore che viene restituito viene utilizzato per ottenere i valori sui quali iterare. L'iteratore di default per le stringhe separa la stringa in caratteri. Dopo lo spreading, vengono inseriti questi valori in un array. Quindi viene svolto nuovamente lo spread sull'array e il risultato viene nuovamente inserito al suo interno. La stringa `'...'` è composta da tre caratteri `.`, quindi la dimensione dell'array risultante è `3`. Ora, passo passo: ```js [...'...'] // -> [ '.', '.', '.' ] [...[...'...']] // -> [ '.', '.', '.' ] [...[...'...']].length // -> 3 ``` Chiaramente, possiamo svolgere questo procedimento di spread e wrap quante volte vogliamo: ```js [...'...'] // -> [ '.', '.', '.' ] [...[...'...']] // -> [ '.', '.', '.' ] [...[...[...'...']]] // -> [ '.', '.', '.' ] [...[...[...[...'...']]]] // -> [ '.', '.', '.' ] // and so on … ``` ## Labels Sono in pochi i programmatori che sono a conoscenza delle Labels in JavaScript. Sono abbastanza interessanti: ```js foo: { console.log("first"); break foo; console.log("second"); } // > first // -> undefined ``` ### 💡 Spiegazione: L'istruzione etichettata viene utilizzata con le istruzioni di `break` o `continue`. Possiamo usare un'etichetta per identificare costrutto iterativo, e usare le istruzioni `break` o `continue` per indicare se il programma deve interrompere l'iterazione o continuarla. Nell'esempio precedente, identifichiamo l'etichetta `foo`. Dopo che `console.log('first');` viene eseguita l'esecuzione viene fermata. Approfondisci le etichette in JavaScript: - [**13.13** Labelled Statements](https://tc39.github.io/ecma262/#sec-labelled-statements) - [Labeled statements](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label) su MDN ## Labels annidate ```js a: b: c: d: e: f: g: 1, 2, 3, 4, 5; // -> 5 ``` ### 💡 Spiegazione: Simile agli esempi precedenti, clicca sui seguenti link: - [**12.16** Comma Operator (`,`)](https://www.ecma-international.org/ecma-262/#sec-comma-operator) - [**13.13** Labelled Statements](https://tc39.github.io/ecma262/#sec-labelled-statements) - [Labeled statements](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label) su MDN ## Un `try..catch` insidioso Cosa restituisce questa espressione? `2` o `3`? ```js (() => { try { return 2; } finally { return 3; } })(); ``` La risposta è `3`. Sorpreso? ### 💡 Spiegazione: - [**13.15** The `try` Statement](https://www.ecma-international.org/ecma-262/#sec-try-statement) ## Si tratta di ereditarietà multipla? Dai uno sguardo all'esempio sottostante: ```js new class F extends (String, Array) {}(); // -> F [] ``` Si tratta di ereditarietà multipla? Negativo. ### 💡 Spiegazione: La parte interessante è il valore della clausola (`(String, Array)`) di `extends`. L'operatore di grouping restituisce sempre il suo ultimo parametro, quindi `(String, Array)` è semplicemente `Array`. Questo significa che abbiamo creato una classe che estende `Array`. - [**14.5** Class Definitions](https://www.ecma-international.org/ecma-262/#sec-class-definitions) - [**12.16** Comma Operator (`,`)](https://www.ecma-international.org/ecma-262/#sec-comma-operator) ## Un generator che produce se stesso Guarda questo esempio di generator che produce se stesso: ```js (function* f() { yield f; })().next(); // -> { value: [GeneratorFunction: f], done: false } ``` Come possiamo notare, il valore restituito è un oggetto con `value` uguale a `f`. In quel caso, possiamo fare una cosa del genere: ```js (function* f() { yield f; })() .next() .value() .next()( // -> { value: [GeneratorFunction: f], done: false } // and again function* f() { yield f; } )() .next() .value() .next() .value() .next()( // -> { value: [GeneratorFunction: f], done: false } // and again function* f() { yield f; } )() .next() .value() .next() .value() .next() .value() .next(); // -> { value: [GeneratorFunction: f], done: false } // and così via // … ``` ### 💡 Spiegazione: Per capirne il suo funzionamento, leggi queste sezioni delle specifiche: - [**25** Control Abstraction Objects](https://www.ecma-international.org/ecma-262/#sec-control-abstraction-objects) - [**25.3** Generator Objects](https://www.ecma-international.org/ecma-262/#sec-generator-objects) ## Una classe di tipo class Considera questa sintassi offuscata in gioco: ```js typeof new class { class() {} }(); // -> 'object' ``` Sembra la dichiarazione di una classe all'interno di un'altra classe. Dovrebbe essere un errore, invece otteniamo `'object'`. ### 💡 Spiegazione: Da ECMAScript 5, possiamo usare le _keywords_ come _property names_. Quindi immaginalo come nel seguente esempio: ```js const foo = { class: function() {} }; ``` ES6 ha standardizzato la definizione compatta per i metodi. Inoltre, le classi possono essere anonime. Quindi se togliamo la parte con `: function`, otteniamo: ```js class { class() {} } ``` Il risultato di una default class è sempre un oggetto semplice. E il tuo typeof dovrebbe restituire `'object'`. Leggi di più qui: - [**14.3** Method Definitions](https://www.ecma-international.org/ecma-262/#sec-method-definitions) - [**14.5** Class Definitions](https://www.ecma-international.org/ecma-262/#sec-class-definitions) ## Oggetti non-coercible Con i ben noti, esiste un modo per evitare la type-coercion. Guarda un po': ```js function nonCoercible(val) { if (val == null) { throw TypeError("nonCoercible should not be called with null or undefined"); } const res = Object(val); res[Symbol.toPrimitive] = () => { throw TypeError("Trying to coerce non-coercible object"); }; return res; } ``` Adesso possiamo utilizzarla in questo modo: ```js // objects const foo = nonCoercible({ foo: "foo" }); foo * 10; // -> TypeError: Trying to coerce non-coercible object foo + "evil"; // -> TypeError: Trying to coerce non-coercible object // strings const bar = nonCoercible("bar"); bar + "1"; // -> TypeError: Trying to coerce non-coercible object bar.toString() + 1; // -> bar1 bar === "bar"; // -> false bar.toString() === "bar"; // -> true bar == "bar"; // -> TypeError: Trying to coerce non-coercible object // numbers const baz = nonCoercible(1); baz == 1; // -> TypeError: Trying to coerce non-coercible object baz === 1; // -> false baz.valueOf() === 1; // -> true ``` ### 💡 Spiegazione: - [A gist by Sergey Rubanov](https://gist.github.com/chicoxyzzy/5dd24608e886adf5444499896dff1197) - [**6.1.5.1** Well-Known Symbols](https://www.ecma-international.org/ecma-262/#sec-well-known-symbols) ## Arrow functions strambe Considera l'esempio sottostante: ```js let f = () => 10; f(); // -> 10 ``` Okay, va bene, ma guarda questo: ```js let f = () => {}; f(); // -> undefined ``` ### 💡 Spiegazione: Potresti aspettarti `{}` anzichè `undefined`. Questo è perchè le parentesi graffe fanno parte della sintassi per le arrow functions, quindi `f` restituirà undefined. È comunque possibile restituire l'oggetto `{}` direttamente da una arrow function, racchiudendo il valore di ritorno tra parentesi. ```js let f = () => ({}); f(); // -> {} ``` ## Arrow functions non possono essere un costruttore Considera l'esempio sottostante: ```js let f = function() { this.a = 1; }; new f(); // -> f { 'a': 1 } ``` Ora, prova a fare la stessa cosa con una arrow function: ```js let f = () => { this.a = 1; }; new f(); // -> TypeError: f is not a constructor ``` ### 💡 Spiegazione: Le arrow function non possono essere utilizzate come costruttore e lanceranno un errore se usate con `new`. Dato che hanno un `this` lessicale, e non hanno la proprietà `prototype`, non avrebbe molto senso. ## `arguments` e arrow functions Considera l'esempio sottostante: ```js let f = function() { return arguments; }; f("a"); // -> { '0': 'a' } ``` Ora, prova a fare la stessa cosa con una arrow function: ```js let f = () => arguments; f("a"); // -> Uncaught ReferenceError: arguments is not defined ``` ### 💡 Spiegazione: Le arrow functions sono una versione alleggerita delle funzioni tradizionali con un focus sull'essere concise e con un `this` lessicale. Allo stesso tempo le arrow function non forniscono un binding per l'oggetto `arguments`. Un'alternativa valida per ottenere lo stesso risultato è utilizzare i `rest parameters`: ```js let f = (...args) => args; f("a"); ``` - [Arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) at MDN. ## Uno strano return anche l'istruzione `return` può essere complicata. Considera questo: <!-- prettier-ignore-start --> ```js (function() { return { b: 10; } })(); // -> undefined ``` <!-- prettier-ignore-end --> ### 💡 Spiegazione: `return` e l'espressione da restituire devono essere sulla stessa linea: ```js (function() { return { b: 10 }; })(); // -> { b: 10 } ``` Questo a causa di un concetto chiamato Automatic Semicolon Insertion, che inserisce automagicamente punti e virgola dopo la maggior parte degli a capo. Nel primo esempio, c'è un punto e virgola inserito tra l'istruzione `return` e l'oggetto, quindi la funzione restituisce `undefined` e l'oggetto non viene mai valutato. - [**11.9.1** Rules of Automatic Semicolon Insertion](https://www.ecma-international.org/ecma-262/#sec-rules-of-automatic-semicolon-insertion) - [**13.10** The `return` Statement](https://www.ecma-international.org/ecma-262/#sec-return-statement) ## Concatenare assegnamenti su un object ```js var foo = { n: 1 }; var bar = foo; foo.x = foo = { n: 2 }; foo.x; // -> undefined foo; // -> {n: 2} bar; // -> {n: 1, x: {n: 2}} ``` Da destra a sinistra, `{n: 2}` viene assegnato a foo, e il risultato di questo assegnamento `{n: 2}` viene assegnato a foo.x, ecco perchè bar è `{n: 1, x: {n: 2}}` in quanto bar è un riferimento a foo. Ma perchè foo.x è undefined mentre bar.x non lo è? ### 💡 Spiegazione: Foo e bar referenziano lo stesso oggetto `{n: 1}`, e gli lvalues vengono risolti prima dell'assegnamento. `foo = {n: 2}` sta creando un nuovo oggetto, quindi foo viene aggiornato per referenziare il nuovo oggetto. Il trick qui è in `foo.x = ...` in quanto il lvalue è stato risolto precedentemente e referenzia ancora il vecchio oggetto `foo = {n: 1}` e lo aggiorna inserendo il valore x. Dopo questa catena di assegnamenti, bar continua a referenziare il vecchio oggetto foo, ma foo referenzia il nuovo oggetto `{n: 2}`, dove x non esiste. È equivalente a: ```js var foo = { n: 1 }; var bar = foo; foo = { n: 2 }; // -> {n: 2} bar.x = foo; // -> {n: 1, x: {n: 2}} // bar.x point to the address of the new foo object // it's not equivalent to: bar.x = {n: 2} ``` ## Accedere alle properties di un object con gli array ```js var obj = { property: 1 }; var array = ["property"]; obj[array]; // -> 1 ``` E per quanto concerne gli array pseudo-multidimensionali? ```js var map = {}; var x = 1; var y = 2; var z = 3; map[[x, y, z]] = true; map[[x + 10, y, z]] = true; map["1,2,3"]; // -> true map["11,2,3"]; // -> true ``` ### 💡 Spiegazione: L'operatore parentesi quadre `[]` converte l'espressione usando il metodo `toString`. Convertire un array di un solo elemento in una stringa è come convertire l'elemento contenuto nell'array in stringa. ```js ["property"].toString(); // -> 'property' ``` ## Nul