diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index 6c5e05c10..80c9fe5a9 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -46,8 +46,12 @@ Il motore applica ottimizzazioni ad ogni passo del processo. Anche durate l'esec Il JavaScript ad giorno d'oggi è un linguaggio di programmazione "sicuro". Non consente alcun accesso a basso livello alla memoria o alla CPU, questo perchè è stato creato con lo scopo di funzionare nei browser, che non richiedono questo tipo di privilegi. +<<<<<<< HEAD Le possibilità dipendono molto dall'ambiente in cui si esegue JavaScript. Ad esempio, [Node.JS](https://wikipedia.org/wiki/Node.js) supporta funzioni che consentono a JavaScript di scrivere/leggere file, eseguire richieste web, etc. +======= +JavaScript's capabilities greatly depend on the environment it's running in. For instance, [Node.js](https://wikipedia.org/wiki/Node.js) supports functions that allow JavaScript to read/write arbitrary files, perform network requests, etc. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb JavaScript integrato nel browser può fare qualsiasi cosa legata alla manipolazione della pagina, interagire con l'utente e con il server. @@ -90,6 +94,10 @@ Ci sono almeno *tre* cose che rendono grande JavaScript: + Operazioni seSupporatomplici vengono eseguite semplicemente. + Supportato dai maggiori browser ed è attivo di default. ``` +<<<<<<< HEAD +======= +JavaScript is the only browser technology that combines these three things. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Queste tre caratteristiche combinate esistono solo in JavaScript e in nessun'altra tecnologia web. diff --git a/1-js/01-getting-started/1-intro/limitations.png b/1-js/01-getting-started/1-intro/limitations.png index 4063bbcf2..3ef6b1e08 100644 Binary files a/1-js/01-getting-started/1-intro/limitations.png and b/1-js/01-getting-started/1-intro/limitations.png differ diff --git a/1-js/01-getting-started/1-intro/limitations@2x.png b/1-js/01-getting-started/1-intro/limitations@2x.png index 55c030ef3..7aa1c41ae 100644 Binary files a/1-js/01-getting-started/1-intro/limitations@2x.png and b/1-js/01-getting-started/1-intro/limitations@2x.png differ diff --git a/1-js/01-getting-started/2-code-editors/article.md b/1-js/01-getting-started/2-code-editors/article.md index 37974ffc1..e9e1f098c 100644 --- a/1-js/01-getting-started/2-code-editors/article.md +++ b/1-js/01-getting-started/2-code-editors/article.md @@ -2,6 +2,7 @@ Un code editor è il posto in cui i programmatori spendo la maggior parte del loro tempo. +<<<<<<< HEAD Ce ne sono due esemplari: IDE ed editor semplici. Molte persone si trovano bene a scegliere uno strumento per ognuno dei due tipi. ## IDE @@ -19,6 +20,24 @@ Se non hai ancora considerato di scegliere un IDE, dai un occhiata a queste alte Tutti gli IDE sono multi-piattaforma. Per Windows, c'e anche l'editor "Visual Studio", da non confondere con "Visual Studio Code". "Visual Studio" è a pagamento ed è un potente editor disponibile solo per Windows, ottimo per piattaforme .NET. E' disponibile una versione gratuita ([Visual Studio Community](https://www.visualstudio.com/vs/community/). +======= +There are two main types of code editors: IDEs and lightweight editors. Many people use one tool of each type. + +## IDE + +The term [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) (Integrated Development Environment) refers to a powerful editor with many features that usually operates on a "whole project." As the name suggests, it's not just an editor, but a full-scale "development environment." + +An IDE loads the project (which can be many files), allows navigation between files, provides autocompletion based on the whole project (not just the open file), and integrates with a version management system (like [git](https://git-scm.com/)), a testing environment, and other "project-level" stuff. + +If you haven't selected an IDE yet, consider the following options: + +- [WebStorm](http://www.jetbrains.com/webstorm/) for frontend development. The same company offers other editors for other languages (paid). +- [Netbeans](http://netbeans.org/) (free). + +All of these IDEs are cross-platform. + +For Windows, there's also "Visual Studio", not to be confused with "Visual Studio Code." "Visual Studio" is a paid and mighty Windows-only editor, well-suited for the .NET platform. A free version of it is called [Visual Studio Community](https://www.visualstudio.com/vs/community/). +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Molti IDE sono a pagamento ma offrono un periodo di prova gratuito. Solitamente il loro costo è trascurabile se paragonato allo stipendio di una sviluppatore qualificato, è quindi importante scegliere il migliore in base alle proprie esigenze. @@ -34,11 +53,19 @@ Nella pratica, gli editor semplici possono avere molti plugin tra cui sintassi a Meritano attenzione le seguenti opzioni: +<<<<<<< HEAD - [Visual Studio Code](https://code.visualstudio.com/) (multi-piattaforma, gratuito). - [Atom](https://atom.io/) (multi-piattaforma, gratuito). - [Sublime Text](http://www.sublimetext.com) (multi-piattaforma, shareware). - [Notepad++](https://notepad-plus-plus.org/) (Windows, gratuito). - [Vim](http://www.vim.org/) e [Emacs](https://www.gnu.org/software/emacs/) sono particolarmente carini se si sanno utilizzare. +======= +- [Visual Studio Code](https://code.visualstudio.com/) (cross-platform, free) also has many IDE-like features. +- [Atom](https://atom.io/) (cross-platform, free). +- [Sublime Text](http://www.sublimetext.com) (cross-platform, shareware). +- [Notepad++](https://notepad-plus-plus.org/) (Windows, free). +- [Vim](http://www.vim.org/) and [Emacs](https://www.gnu.org/software/emacs/) are also cool if you know how to use them. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## I miei preferiti @@ -46,8 +73,13 @@ La personale preferenza dell'autore è di avere entrambi, un IDE per il progetto Io personalmente utilizzo: +<<<<<<< HEAD - [WebStorm](http://www.jetbrains.com/webstorm/) per JS, anche se ci sono più linguaggi in un progetto posso intercambiarli tramite JetBrains. - Come editor semplice -- [Sublime Text](http://www.sublimetext.com) o [Atom](https://atom.io/). +======= +- As an IDE for JS -- [WebStorm](http://www.jetbrains.com/webstorm/) (I switch to one of the other JetBrains offerings when using other languages) +- As a lightweight editor -- [Sublime Text](http://www.sublimetext.com) or [Atom](https://atom.io/). +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Non intestarditevi @@ -55,4 +87,8 @@ Gli editor elencanti sopra sono sono quelli che io e i miei amici, che considero Ci sono altri grandi edito nel nostro grande mondo. Scegli quindi quello che più ti si addice. +<<<<<<< HEAD La scelta di un edito, come degli altri strumenti, è individuale e dipende dai progetti, abitudini e preferenze personali. +======= +The choice of an editor, like any other tool, is individual and depends on your projects, habits, and personal preferences. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/01-getting-started/3-devtools/article.md b/1-js/01-getting-started/3-devtools/article.md index 572851ff1..e369aa746 100644 --- a/1-js/01-getting-started/3-devtools/article.md +++ b/1-js/01-getting-started/3-devtools/article.md @@ -1,15 +1,27 @@ # Developer console +<<<<<<< HEAD Il codice è incline a contenere errori. E' molto probabile che tu commetta errori... Di cosa sto parlando? *Sicuramente* commetterai errori, sempre che tu sia umano, e non un [robot](https://en.wikipedia.org/wiki/Bender_(Futurama)). In un browser però, l'utente non può vedere gli errori. Quindi se qualcosa non funziona nello script, noi non possiamo vedere cosa non va e non possiamo perciò sistemarlo. +======= +Code is prone to errors. You will quite likely make errors... Oh, what am I talking about? You are *absolutely* going to make errors, at least if you're a human, not a [robot](https://en.wikipedia.org/wiki/Bender_(Futurama)). + +But in the browser, users don't see errors by default. So, if something goes wrong in the script, we won't see what's broken and can't fix it. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Per poter visualizzare gli errori e ricevere altre informazioni utili riguardo gli script, i browser integrano degli "strumenti di sviluppo". +<<<<<<< HEAD Molto spesso gli sviluppatori tendono ad utilizzare Chrome o Firefox poichè questi browser forniscono i migliori strumenti per lo sviluppo. Anche gli altri browser contengono gli strumenti per lo sviluppo, talvolta con caratteristiche speciali, ma più che altro giocano a "prendere" Chrome e Firefox. Quindi molte perone hanno un browser "preferito" e utilizzano gli altri solo quando un problema è specifico di quel browser. Gli strumenti di sviluppo sono potenti; hanno molte caratteristiche. Prima di tutto, dobbiamo capire come ottenerli, come cercare errori e come eseguire comandi JavaScript. +======= +Most developers lean towards Chrome or Firefox for development because those browsers have the best developer tools. Other browsers also provide developer tools, sometimes with special features, but are usually playing "catch-up" to Chrome or Firefox. So most developers have a "favorite" browser and switch to others if a problem is browser-specific. + +Developer tools are potent; they have many features. To start, we'll learn how to open them, look at errors, and run JavaScript commands. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Google Chrome @@ -32,28 +44,53 @@ Il look esatto degli strumenti di sviluppo dipenderà dalla tua versione di Chro Sotto il messaggio d'errore, c'e un simbolo blu `>`. Questo indica la "riga di comando" dove noi possiamo digitare dei comandi JavaScript. Premendo `key:Enter` si eseguono (`key:Shift+Enter` per inserire comandi multi-linea). +<<<<<<< HEAD Adesso possiamo visualizzare gli errori, ed è già abbastanza come inizio. Ritorneremo sugli strumenti di sviluppo più avanti e analizzeremo il debugging più in profondità nel capitolo . +======= +Now we can see errors, and that's enough for a start. We'll come back to developer tools later and cover debugging more in-depth in the chapter . +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Firefox, Edge, and others Molti altri browser utilizzano `key:F12` per aprire gli strumenti di sviluppo. +<<<<<<< HEAD Lo stile è comunque molto simile. Quando avrai imparato come utilizzare uno di questi strumenti (puoi iniziare con quelli di Chrome), potrai facilmente passare agli altri. +======= +The look & feel of them is quite similar. Once you know how to use one of these tools (you can start with Chrome), you can easily switch to another. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Safari Safari (Mac browser, non supportato da Windows/Linux) è un pò speciale in questo ambito. E' necessario attivare prima il "Menu di Sviluppo". +<<<<<<< HEAD Apri le Impostazioni e vai sul pannello "Avanzate". In basso troverai un'opzione da spuntare: +======= +Open Preferences and go to the "Advanced" pane. There's a checkbox at the bottom: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ![safari](safari.png) Adesso tramite `key:Cmd+Opt+C` puoi attivare/disattivare la console. Inoltre noterai che un nuovo menu "Sviluppo" è apparso. Contiene molti comandi e opzioni. +<<<<<<< HEAD ## Riepilogo - Gli strumenti di sviluppo ci consentono di trovare gli errori, eseguire comandi, esaminare variabili e molto altro. - Possono essere aperti con `key:F12` per la maggior parte dei browser in Windows. Chrome su Mac `key:Cmd+Opt+J`, Safari: `key:Cmd+Opt+C` (avendolo precedentemente abilitato). +======= +## Multi-line input + +Usually, when we put a line of code into the console, and then press `key:Enter`, it executes. + +To insert multiple lines, press `key:Shift+Enter`. + +## Summary + +- Developer tools allow us to see errors, run commands, examine variables, and much more. +- They can be opened with `key:F12` for most browsers on Windows. Chrome for Mac needs `key:Cmd+Opt+J`, Safari: `key:Cmd+Opt+C` (need to enable first). +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Adesso abbiamo l'ambiente di sviluppo pronto. Nella prossima sezione inizieremo ad analizzare JavaScript. diff --git a/1-js/02-first-steps/01-hello-world/article.md b/1-js/02-first-steps/01-hello-world/article.md index 868a71c9c..20c53e995 100644 --- a/1-js/02-first-steps/01-hello-world/article.md +++ b/1-js/02-first-steps/01-hello-world/article.md @@ -1,16 +1,30 @@ # Hello, world! +<<<<<<< HEAD Il seguente tutorial tratta del core(nucleo) JavaScript, che è indipendente dalla piattaforma. Inoltre, più avanti, imparerai Node.JS e altre piattaforme che ne fanno utilizzo. Abbiamo bisogno di un ambiente di lavoro per eseguire i nostri script, e il fatto che questo libro sia online, rende il browser un ottima scelta. Cercheremo di mantenere al minimo l'utilizzo dei comandi specifici per browser (come `alert`), cosi non dovrai perdere la testa se deciderai di spostarti in altri ambienti come Node.JS. In ogni caso, i dettagli browser vengono spiegati in dettaglio nella [prossima parte](/ui) del tutorial. Quindi prima di tutto, vediamo come inserire uno script in una pagina web. Per ambienti server-side, è sufficiente eseguirli con un comando come `"node my.js"` in Node.JS. +======= +The tutorial that you're reading is about core JavaScript, which is platform-independent. Later on, you'll learn about Node.js and other platforms that use it. + +But we need a working environment to run our scripts and, since this book is online, the browser is a good choice. We'll keep the amount of browser-specific commands (like `alert`) to a minimum so that you don't spend time on them if you plan to concentrate on another environment (like Node.js). We'll focus on JavaScript in the browser in the [next part](/ui) of the tutorial. + +So first, let's see how we attach a script to a webpage. For server-side environments (like Node.js), you can execute the script with a command like `"node my.js"`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Il tag "script" I programmi JavaScript possono essere inseriti in qualunque parte di un documento HTML, con l'utilizzo del tag ` ``` +<<<<<<< HEAD Questo trucco non viene più utilizzato nel moderno JavaScript. I commenti venivano utilizzati per nascondere il codice JavaScript dai vecchi browser che non conoscevano il tag ` ``` +<<<<<<< HEAD Questo `/path/to/script.js` è il percorso assoluto al file che contiene lo script (dalla root del sito). E' anche possibile fornire un percorso relativo a partire dalla pagina corrente. Per esempio `src="script.js"` significa che il file `"script.js"` si trova nella cartella corrente. +======= +Here, `/path/to/script.js` is an absolute path to the script file (from the site root). + +You can also provide a relative path from the current page. For instance, `src="script.js"` would mean a file `"script.js"` in the current folder. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Possiamo anche fornire un URL. Per esempio: @@ -92,6 +143,7 @@ Per integrare più script, utilzzate più volte il tag: ```smart Come regola da seguire, solo gli script molto semplici vanno inseriti all'interno dell'HTML. Quelli più complessi vanno inseriti in file separati. +<<<<<<< HEAD Il beneficio di inserire gli script in file separati è che il browser andrà a scaricarli e li memorizzerà nella sua [cache](https://en.wikipedia.org/wiki/Web_cache). Cosi facendo, le altre pagine che vorranno utilizzare lo stesso script lo preleveranno dallacache invece che riscaricarlo. Quindi il file verrà scaricato una sola volta. @@ -101,6 +153,17 @@ Questo risparmierà traffico e renderà le pagine più veloci. ````warn header="Se `src` è impostato, il contenuto all'interno di script verrà ignorato." Quindi un tag ` ``` +<<<<<<< HEAD Dobbiamo scegliere: o esterno ` ``` +<<<<<<< HEAD ## Riepilogo +======= +- We can use a ``. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb - Possiamo usare il tag ``. +<<<<<<< HEAD C'e ancora molto da imparare riguaro gli script browser e la loro interazione con le pagine web. Ma tenete a mente che questa parte del tutorial è dedicata al linguaggio JavaScript, quindi non dobbiamo distrarci da questo obbiettivo. Andremo ad utilizzare il browser come piattaforma in cui eseguire JavaScript, che è molto utile, ma è solo uno dei tanti modi. +======= +There is much more to learn about browser scripts and their interaction with the webpage. But let's keep in mind that this part of the tutorial is devoted to the JavaScript language, so we shouldn't distract ourselves with browser-specific implementations of it. We'll be using the browser as a way to run JavaScript, which is very convenient for online reading, but only one of many. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/01-hello-world/hello-world-render.png b/1-js/02-first-steps/01-hello-world/hello-world-render.png index ffe810697..ba94e017d 100644 Binary files a/1-js/02-first-steps/01-hello-world/hello-world-render.png and b/1-js/02-first-steps/01-hello-world/hello-world-render.png differ diff --git a/1-js/02-first-steps/01-hello-world/hello-world-render@2x.png b/1-js/02-first-steps/01-hello-world/hello-world-render@2x.png index c4411027c..9b2d4479f 100644 Binary files a/1-js/02-first-steps/01-hello-world/hello-world-render@2x.png and b/1-js/02-first-steps/01-hello-world/hello-world-render@2x.png differ diff --git a/1-js/02-first-steps/02-structure/article.md b/1-js/02-first-steps/02-structure/article.md index 8ee513608..73a5a6542 100644 --- a/1-js/02-first-steps/02-structure/article.md +++ b/1-js/02-first-steps/02-structure/article.md @@ -1,21 +1,38 @@ # Struttura del codice +<<<<<<< HEAD La prima cosa da studiare riguarda il come è strutturato il codice. +======= +The first thing we'll study is the building blocks of code. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Istruzioni Le istruzioni sono dei costrutti sintattici e comandi che eseguono azioni. +<<<<<<< HEAD Abbiamo già visto un'istruzione `alert('Hello, world!')`, che mostra il messaggio "Hello world!". All'interno del codice possiamo avere tutte le istruzioni che desideriamo. Una seconda istruzione può essere separata tramite il punto e virgola. Ad esempio, qui dividiamo il messaggio in due: +======= +We've already seen a statement, `alert('Hello, world!')`, which shows the message "Hello, world!". + +We can have as many statements in our code as we want. Statements can be separated with a semicolon. + +For example, here we split "Hello World" into two alerts: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run no-beautify alert('Hello'); alert('World'); ``` +<<<<<<< HEAD Di solito ogni istruzione viene scritta in una linea separata -- questo rende il codice molto più leggibile: +======= + +Usually, statements are written on separate lines to make the code more readable: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run no-beautify alert('Hello'); @@ -33,11 +50,19 @@ alert('Hello') alert('World') ``` +<<<<<<< HEAD Qui JavaScript interpreta la fine della riga come un punto e virgola "implicito". Viene anche chiamato [automatic semicolon insertion](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion). **In molti casi la nuova riga viene interpretata come un punto e virgola implicito. Ma "in molti casi" non significa "sempre"!** Ci sono casi in cui la nuova riga non implica una punto e virgola, per esempio: +======= +Here, JavaScript interprets the line break as an "implicit" semicolon. This is called an [automatic semicolon insertion](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion). + +**In most cases, a newline implies a semicolon. But "in most cases" does not mean "always"!** + +There are cases when a newline does not mean a semicolon. For example: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run no-beautify alert(3 + @@ -58,9 +83,15 @@ Se sei curioso di vedere un esempio concreto di questo tipo di errore, dai un oc [1, 2].forEach(alert) ``` +<<<<<<< HEAD Non c'e bisogno di pensare al significato delle parentesi `[]` e al `forEach`. Li studieremo più avanti, per ora non hanno importanza. Sappiate che il risultato mostra `1` e poi `2`. Adesso andiamo ad aggiungere un `alert` prima del codice e *non* concludiamo la riga con il punto e virgola: +======= +No need to think about the meaning of the brackets `[]` and `forEach` yet. We'll study them later. For now, just remember the result of the code: it shows `1` then `2`. + +Now, let's add an `alert` before the code and *not* finish it with a semicolon: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run no-beautify alert("There will be an error") @@ -68,7 +99,11 @@ alert("There will be an error") [1, 2].forEach(alert) ``` +<<<<<<< HEAD Adesso se noi lo eseguiamo, solo il primo `alert` viene mostrato, poi avremmo un errore! +======= +Now if we run the code, only the first `alert` is shown and then we have an error! +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ma tutto si risolve se aggiungiamo un punto e virgola dopo `alert`: ```js run @@ -77,27 +112,49 @@ alert("All fine now"); [1, 2].forEach(alert) ``` +<<<<<<< HEAD Adesso avremmo il messaggio "All fine now" e successivamente `1` seguito da `2`. L'errore nel non aver messo il punto e virgola è avvenuto perchè JavaScript non implica un punto e virgola prima delle parentesi quadre `[...]`. Quindi, poichè il punto e virgola non viene auto-inserito, il codice del precedente esempio viene trattato come un istruzione singola. Quindi il motore JavaScript lo vede cosi: +======= +Now we have the "All fine now" message followed by `1` and `2`. + + +The error in the no-semicolon variant occurs because JavaScript does not assume a semicolon before square brackets `[...]`. + +So, because the semicolon is not auto-inserted, the code in the first example is treated as a single statement. Here's how the engine sees it: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run no-beautify alert("There will be an error")[1, 2].forEach(alert) ``` +<<<<<<< HEAD Anche se dovrebbero essere due istruzioni separate, non una singola. But it should be two separate statements, not a single one. Questo tipo di unione, in questo caso è errata, quindi produce un errore. Ci sono altre situazioni in cui si verifica questo tipo di errore. ```` E' consigliato quindi, di mettere il punto e virgola fra ogni istruzione, anche se vengono scritte in righe diverse. Questa regola è largamente adottata dalla community. Ripetiamolo nuovamente -- *è possibile* evitare di scrivere il punto e virgola la maggior parte delle volte. Ma è più sicuro -- specialmente per un beginner -- inserirle al termine di ogni istruzione. +======= +But it should be two separate statements, not one. Such a merging in this case is just wrong, hence the error. This can happen in other situations. +```` + +We recommend putting semicolons between statements even if they are separated by newlines. This rule is widely adopted by the community. Let's note once again -- *it is possible* to leave out semicolons most of the time. But it's safer -- especially for a beginner -- to use them. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Commenti +<<<<<<< HEAD Con il passare del tempo, i programmi sono diventati sempre più complessi. E' diventato necessario aggiungere *commenti* che descrivessero i comportamenti del codice e il perchè. I commenti possono essere messi in qualsiasi punto dello script. Infatti non hanno alcun effetto sull'esecuzione del codice, poichè il motore JavaScript semplicemente li ignora. +======= +As time goes on, programs become more and more complex. It becomes necessary to add *comments* which describe what the code does and why. + +Comments can be put into any place of a script. They don't affect its execution because the engine simply ignores them. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb **I commenti su una singola linea incominciano con due caratteri di slash `//`.** @@ -123,7 +180,13 @@ alert('Hello'); alert('World'); ``` +<<<<<<< HEAD Il contenuto dei commenti viene ignorato, quindi se inseriamo codice al suo interno /* ... */ non verrà eseguito. +======= +The content of comments is ignored, so if we put code inside /* ... */, it won't execute. + +Sometimes it can be handy to temporarily disable a part of code: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Qualche volta diventa utile per bloccare temporaneamente qualche porzione di codice: ```js run @@ -133,8 +196,13 @@ alert('Hello'); alert('World'); ``` +<<<<<<< HEAD ```smart header="Usa le scorciatoie da tastiera!" In molti editor una linea di codice può essere commentata con la combinazione da tastiera dei tasti `key:Ctrl+/` e una combinazione simile a `key:Ctrl+Shift+/` -- per i commenti multilinea (selezionate prima una parte di codice e poi utilizzate la combinazione di tasti). Su Mac dovrebbe funzionare la combinazione `key:Cmd` al posto di `key:Ctrl`. +======= +```smart header="Use hotkeys!" +In most editors, a line of code can be commented out by pressing the `key:Ctrl+/` hotkey for a single-line comment and something like `key:Ctrl+Shift+/` -- for multiline comments (select a piece of code and press the hotkey). For Mac, try `key:Cmd` instead of `key:Ctrl`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ``` ````warn header="I commenti annidati non sono supportati!" @@ -151,6 +219,14 @@ alert( 'World' ); ```` Non abbiate paura di utilizzare i commenti nel codice. +<<<<<<< HEAD I commenti aumenteranno il peso finale dello script, ma questo non sarà un problema. Ci sono tantissimi strumenti che possono minimizzare("minify") il codice prima di pubblicarlo nel server. Questi strumenti rimuovono i commenti, quindi non appariranno nello script che verrà eseguito. Perciò i commenti non hanno alcun effetto negativo sul codice prodotto. Inoltre più avanti nel tutorial ci sarà un capitolo che illustrerà come scrivere commenti in maniera ottimale. +======= +Please, don't hesitate to comment your code. + +Comments increase the overall code footprint, but that's not a problem at all. There are many tools which minify code before publishing to a production server. They remove comments, so they don't appear in the working scripts. Therefore, comments do not have negative effects on production at all. + +Later in the tutorial there will be a chapter that also explains how to write better comments. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/03-strict-mode/article.md b/1-js/02-first-steps/03-strict-mode/article.md index 3a3da6964..7dcbaefc4 100644 --- a/1-js/02-first-steps/03-strict-mode/article.md +++ b/1-js/02-first-steps/03-strict-mode/article.md @@ -1,5 +1,6 @@ # Le tecniche moderne, "use strict" +<<<<<<< HEAD Per molto tempo JavaScript si è evoluto senza problemi di compatibilità. Nuove funzionalità venivano aggiunte al linguaggio, ma quelle vecchie non cambiavano. Questo ha consentito al vecchio codice di non diventare obsoleto. Ma lo svantaggio è stato che cosi facendo gli errori e le decisioni imperefette fatte dai creatori di JavaScript, rimarranno nel linguaggio per sempre. @@ -11,6 +12,19 @@ Cosi è stato fino al 2009 quando è apparsa ECMAScript 5 (ES5). Ha aggiungo nuo La direttiva è simile ad una stringa: `"use strict"` o `'use strict'`. Quando viene inserita all'inizio dello script, da quel momento l'intero script funziona nello stile "moderno". Per esempio: +======= +For a long time, JavaScript evolved without compatibility issues. New features were added to the language while old functionality didn't change. + +That had the benefit of never breaking existing code. But the downside was that any mistake or an imperfect decision made by JavaScript's creators got stuck in the language forever. + +This was the case until 2009 when ECMAScript 5 (ES5) appeared. It added new features to the language and modified some of the existing ones. To keep the old code working, most modifications are off by default. You need to explicitly enable them with a special directive: `"use strict"`. + +## "use strict" + +The directive looks like a string: `"use strict"` or `'use strict'`. When it is located at the top of a script, the whole script works the "modern" way. + +For example: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js "use strict"; @@ -21,16 +35,27 @@ Per esempio: Impareremo presto le funzioni(un modo per raggruppare le istruzioni). +<<<<<<< HEAD Guardando avanti, notiamo che `"use strict"` può essere applicato all'inizio di una funzione(la maggior parte) piuttosto che all'intero script. La modalità strict sarà quindi attiva solo all'interno di quella funzione. Solitamente si utilizza nell'intero script. +======= +Looking ahead, let's just note that `"use strict"` can be put at the start of most kinds of functions instead of the whole script. Doing that enables strict mode in that function only. But usually, people use it for the whole script. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ````warn header="Assicurati che \"use strict\" sia all'inizio" Assicurati `"use strict"` sia all'inizio dello script, altrimenti la modalità script non verrà abilitata. +<<<<<<< HEAD Qui non si attiva la modalità strict: +======= +````warn header="Ensure that \"use strict\" is at the top" +Please make sure that `"use strict"` is at the top of your scripts, otherwise strict mode may not be enabled. + +Strict mode isn't enabled here: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js no-strict alert("some code"); -// "use strict" below is ignored, must be on the top +// "use strict" below is ignored--it must be at the top "use strict"; @@ -40,14 +65,40 @@ alert("some code"); Solo i commenti possono apparire prima di `"use strict"`. ```` +<<<<<<< HEAD ```warn header="Non c'e nessun modo per annullare `use strict`" Non esiste nessuna direttiva `"no use strict"` o simile, che possa riportare lo script alla vecchia modalità. Una volta abilitata la modalità strict, non c'e ritorno. +======= +```warn header="There's no way to cancel `use strict`" +There is no directive like `"no use strict"` that reverts the engine to old behavior. + +Once we enter strict mode, there's no return. +``` + +## Browser console + +For the future, when you use a browser console to test features, please note that it doesn't `use strict` by default. + +Sometimes, when `use strict` makes a difference, you'll get incorrect results. + +Even if we press `key:Shift+Enter` to input multiple lines, and put `use strict` on top, it doesn't work. That's because of how the console executes the code internally. + +The reliable way to ensure `use strict` would be to input the code into console like this: + +```js +(function() { + 'use strict'; + + // ...your code... +})() +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ``` ## Usare sempre "use strict" +<<<<<<< HEAD Le differenze tra `"use strict"` contro la modalità di "default" non è ancora stata spiegata. Nel prossimo capitolo, mentre impareremo le caratteristiche del linguaggio, terremo d'occhio anche le differenze tra la modalità strict e quella di default. Fortunatamente non sono molte. E senza dubbio renderanno migliore la scrittura del codice. @@ -57,4 +108,16 @@ A questo punto è sufficiente sapere le regole generali: 1. L'utilizzo della direttiva `"use strict"` cambia la modalità del motore JavaScript al il metodo moderno, cambiando i comportamenti di alcune caratteristiche integrate. Vedremo meglio i dettagli man mano che studiamo. 2. La modalità strict viene abilitata tramite l'istruzione `"use strict"` posta all'inizio. In ogni caso ci sono alcune caratteristiche del linguaggio come "classi" e "moduli" che attivano la modalità strict in automatico. 3. La modalità strict è supportata da tutti i moderni browser. -4. E' sempre raccomandato iniziare lo script con `"use strict"`. Tutti gli esempi del tutorial assumo che esso sia attivo, tranne in alcuni esempi(veramente pochi) in cui verrà specificato esplicitamente. \ No newline at end of file +4. E' sempre raccomandato iniziare lo script con `"use strict"`. Tutti gli esempi del tutorial assumo che esso sia attivo, tranne in alcuni esempi(veramente pochi) in cui verrà specificato esplicitamente. +======= +We have yet to cover the differences between strict mode and the "default" mode. + +In the next chapters, as we learn language features, we'll note the differences between the strict and default modes. Luckily, there aren't many and they actually make our lives better. + +For now, it's enough to know about it in general: + +1. The `"use strict"` directive switches the engine to the "modern" mode, changing the behavior of some built-in features. We'll see the details later in the tutorial. +2. Strict mode is enabled by placing `"use strict"` at the top of a script or function. Several language features, like "classes" and "modules", enable strict mode automatically. +3. Strict mode is supported by all modern browsers. +4. We recommended always starting scripts with `"use strict"`. All examples in this tutorial assume strict mode unless (very rarely) specified otherwise. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/04-variables/2-declare-variables/task.md b/1-js/02-first-steps/04-variables/2-declare-variables/task.md index 190d75c97..07bd26c54 100644 --- a/1-js/02-first-steps/04-variables/2-declare-variables/task.md +++ b/1-js/02-first-steps/04-variables/2-declare-variables/task.md @@ -4,5 +4,10 @@ importance: 3 # Scegliere il giusto nome +<<<<<<< HEAD 1. Create una variabile con il nome del nostro pianeta. Come chiameresti questo tipo di variabile? 2. Create una variabile che memorizza il nome del visitatore corrente. Come chiameresti questa variabile? +======= +1. Create a variable with the name of our planet. How would you name such a variable? +2. Create a variable to store the name of a current visitor to a website. How would you name that variable? +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/04-variables/article.md b/1-js/02-first-steps/04-variables/article.md index 6ab3d6122..e2e35c1d3 100644 --- a/1-js/02-first-steps/04-variables/article.md +++ b/1-js/02-first-steps/04-variables/article.md @@ -1,16 +1,28 @@ # Variabili +<<<<<<< HEAD La maggior parte delle volte, le applicazioni JavaScript necessitano d'informazione per poter lavorare. Vediamo due esempi: 1. Un negozio online -- le informazioni possono riguardare i beni venduti e il carrello. 2. Un applicazione di messaggistica -- le informazioni possono riguardare utenti, messaggi e molto altro. +======= +Most of the time, a JavaScript application needs to work with information. Here are two examples: +1. An online shop -- the information might include goods being sold and a shopping cart. +2. A chat application -- the information might include users, messages, and much more. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Le variabili vengono utilizzate per memorizzare informazioni. ## Variabile +<<<<<<< HEAD Una variabile [variable](https://en.wikipedia.org/wiki/Variable_(computer_science)) è un "memorizzatore con nome" per i dati. Possiamo usare le variabile per memorizzare informazioni extra, visitatori e altri dati. Per creare una variabile in JavaScript, dobbiamo utilizzare la parola chiave `let`. +======= +A [variable](https://en.wikipedia.org/wiki/Variable_(computer_science)) is a "named storage" for data. We can use variables to store goodies, visitors, and other data. + +To create a variable in JavaScript, use the `let` keyword. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb L'istruzione sotto crea(in altre parole: *dichiara* o *definisce*) una variabile identificata dal nome "messaggio": @@ -18,7 +30,11 @@ L'istruzione sotto crea(in altre parole: *dichiara* o *definisce*) una variabile let message; ``` +<<<<<<< HEAD Adesso possiamo inserirci dati utilizzando l'operatore di assegnazione `=`: +======= +Now, we can put some data into it by using the assignment operator `=`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js let message; @@ -39,7 +55,11 @@ alert(message); // shows the variable content */!* ``` +<<<<<<< HEAD Per essere precisi, potremmo unire la dichiarazione e l'assegnazione in una singola riga: +======= +To be concise, we can combine the variable declaration and assignment into a single line: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let message = 'Hello!'; // define the variable and assign the value @@ -53,7 +73,11 @@ Possiamo anche dichiarare più variabili in una riga: let user = 'John', age = 25, message = 'Hello'; ``` +<<<<<<< HEAD Questo potrebbe risultare più breve, ma è sconsigliato. Per mantenere una migliore leggibilità è meglio utilizzare una riga per ogni variabile. +======= +That might seem shorter, but we don't recommend it. For the sake of better readability, please use a single line per variable. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb L'alternativa a più righe è un po più lunga, ma più facile da leggere: @@ -63,7 +87,11 @@ let age = 25; let message = 'Hello'; ``` +<<<<<<< HEAD Alcune persone scrivono variabili multiple in questo modo: +======= +Some people also define multiple variables in this multiline style: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js no-beautify let user = 'John', age = 25, @@ -78,18 +106,32 @@ let user = 'John' , message = 'Hello'; ``` +<<<<<<< HEAD Tecnicamente, tutte queste varianti fanno la stessa cosa. Quindi è una questione di gusto personale ed estetico. ````smart header="`var` piuttosto che `let`" Nei vecchi script potresti trovare: `var` piuttosto che `let`: +======= +Technically, all these variants do the same thing. So, it's a matter of personal taste and aesthetics. + + +````smart header="`var` instead of `let`" +In older scripts, you may also find another keyword: `var` instead of `let`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js *!*var*/!* message = 'Hello'; ``` +<<<<<<< HEAD La parola chiave `var` è *quasi* la stessa cosa di `let`. Dichiara comunque una variabile, ma in un maniera leggermente diversa, "vecchio stile". Ci sono delle sottili differenze tra `let` e `var`, ma per ora non hanno importanza. Le copriremo in dettaglio più avanti, nel capitolo . +======= +The `var` keyword is *almost* the same as `let`. It also declares a variable, but in a slightly different, "old-school" way. + +There are subtle differences between `let` and `var`, but they do not matter for us yet. We'll cover them in detail in the chapter . +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```` ## Un analogia con il mondo reale @@ -100,10 +142,15 @@ Per esempio, la variabile `message` può essere immaginata come una scatola con ![](variable.png) +<<<<<<< HEAD Possiamo inserire qualsiasi valore all'interno della scatola. Possiamo ance cambiarlo. Il valore può cambiare tutte le volte che ne abbiamo bisogno: +======= +We can put any value in the box. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb +We can also change it as many times as we want: ```js run let message; @@ -134,29 +181,51 @@ alert(hello); // Hello world! alert(message); // Hello world! ``` +<<<<<<< HEAD ```smart header="Linguaggi funzionali" Può essere interessante sapere che esistono anche linguaggi di programmazione [funzionale](https://en.wikipedia.org/wiki/Functional_programming) che vietano di cambiare il valore di una variabile. Per esempio, [Scala](http://www.scala-lang.org/) o [Erlang](http://www.erlang.org/). +======= +```smart header="Functional languages" +It's interesting to note that [functional](https://en.wikipedia.org/wiki/Functional_programming) programming languages, like [Scala](http://www.scala-lang.org/) or [Erlang](http://www.erlang.org/), forbid changing variable values. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb In questo tipo di linguaggi, una volta che il valore viene memorizzato "dentro la scatola", ci rimane per sempre. Se abbiamo bisogno di memorizzare qualcos altro, il linguaggio ci forza a creare una nuova scatola (dichiarare una nuova variabile). Non possiamo quindi riutilizzare quelle vecchie. +<<<<<<< HEAD Anche se potrebbero sembrare un pò strano a prima vista, questi linguaggi sono veramente capaci di sviluppare grandi cose. Inoltre, ci sono certe situazioni come calcoli paralleli in cui questi limiti portano dei benefici. Studiare un linguaggio di questo tipo (anche se non abbiamo intenzione di utilizzarlo a breve) è consigliato per allargare le proprie conoscenze. +======= +Though it may seem a little odd at first sight, these languages are quite capable of serious development. More than that, there are areas like parallel computations where this limitation confers certain benefits. Studying such a language (even if you're not planning to use it soon) is recommended to broaden the mind. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ``` ## Nomi delle variabili [#variable-naming] +<<<<<<< HEAD In JavaScript ci sono solo due limitazioni per il nome delle variabili: 1. Il nome deve contenere solo lettere, numeri, simboli `$` e `_`. 2. Il primo carattere non può essere un numero. Esempi di nomi validi: +======= +There are two limitations on variable names in JavaScript: + +1. The name must contain only letters, digits, or the symbols `$` and `_`. +2. The first character must not be a digit. + +Examples of valid names: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js let userName; let test123; ``` +<<<<<<< HEAD Quando il nome contiene più parole, viene utilizzato il [camelCase](https://en.wikipedia.org/wiki/CamelCase). La logica è: le parole vanno una dopo l'altra, ogni parola inizia con lettere maiuscola: `myVeryLongName`. +======= +When the name contains multiple words, [camelCase](https://en.wikipedia.org/wiki/CamelCase) is commonly used. That is: words go one after another, each word starting with a capital letter: `myVeryLongName`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Una cosa interessante è che -- il simbolo del dollaro `'$'` e l'underscore `'_'` possono essere utilizzati nei nomi. Sono dei semplici simboli, come le lettere, senza alcun significato speciale. @@ -174,11 +243,16 @@ Questi invece non lo sono: ```js no-beautify let 1a; // cannot start with a digit -let my-name; // a hyphen '-' is not allowed in the name +let my-name; // hyphens '-' aren't allowed in the name ``` +<<<<<<< HEAD ```smart header="La questione delle lettere" Le variabili `apple` ed `AppLE` -- sono due variabili distinte. +======= +```smart header="Case matters" +Variables named `apple` and `AppLE` are two different variables. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ``` ````smart header="Le lettere non inglesi sono permesse, ma sono sconsigliate" @@ -192,10 +266,17 @@ let 我 = '...'; Tecnicamente, non ci sono errori, questo tipo di nomi sono permessi, ma la tradizione internazionale è di utilizzare l'alfabeto inglese per il nome delle variabili. Anche se stiamo scrivendo un piccolo script, potrebbe infatti avere una lunga vita. Persone di altre nazionalità potrebbero aver bisogno di leggerlo. ```` +<<<<<<< HEAD ````warn header="Nomi riservati" C'e una [lista di parole riservate](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords), che non possono essere utilizzare come nomi di variabili, perchè vengono utilizzate dal linguaggio stesso. Per esempio, le parole `let`, `class`, `return`, `function` sono riservate. +======= +````warn header="Reserved names" +There is a [list of reserved words](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords), which cannot be used as variable names because they are used by the language itself. + +For example: `let`, `class`, `return`, and `function` are reserved. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Questo codice provocherà un errore di sintassi: @@ -207,17 +288,25 @@ let return = 5; // also can't name it "return", error! ````warn header="Un assegnazione senza `use strict`" +<<<<<<< HEAD Normalmente, abbiamo bisogno di definire variabili prima di utilizzarle. Ma una volta, era possibile definire una variabile semplicemente assegnandogli un valore, senza `let`. Questo è ancora possibile se non utilizziamo `use strict`. E' necessario per mantenere la compatibilità con i vecchi script. +======= +Normally, we need to define a variable before using it. But in the old times, it was technically possible to create a variable by a mere assignment of the value without using `let`. This still works now if we don't put `use strict` in our scripts to maintain compatibility with old scripts. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run no-strict // note: no "use strict" in this example -num = 5; // the variable "num" is created if didn't exist +num = 5; // the variable "num" is created if it didn't exist alert(num); // 5 ``` +<<<<<<< HEAD Però è una pessima pratica, se inseriamo "use script" provocherà un errore: +======= +This is a bad practice and would cause an error in strict mode: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js "use strict"; @@ -230,13 +319,21 @@ num = 5; // error: num is not defined ## Constanti +<<<<<<< HEAD Per dichiarare una variabile costante(immutabile), dobbiamo utilizzare `const` invece di `let`: +======= +To declare a constant (unchanging) variable, use `const` instead of `let`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js const myBirthday = '18.04.1982'; ``` +<<<<<<< HEAD Le variabili dichiarate con `const` vengono chiamate "costanti". Non possono cambiare valore. Se tentassimo di farlo verrebbe sollevato un errore: +======= +Variables declared using `const` are called "constants". They cannot be changed. An attempt to do so would cause an error: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run const myBirthday = '18.04.1982'; @@ -245,7 +342,12 @@ myBirthday = '01.01.2001'; // error, can't reassign the constant! ``` Quando il programmatore è sicuro che il valore della variabile non cambierà mai, può utilizzare `const` per soddisfare questa esigenza, e metterlo anche in mostra a tutti gli altri. +<<<<<<< HEAD ### Le costanti maiuscole +======= +When a programmer is sure that a variable will never change, they can declare it with `const` to guarantee and clearly communicate that fact to everyone. + +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Una pratica molto diffusa è di utilizzare le variabili costanti come alias di valori difficili da ricordare, e che sono noti prima dell'esecuzione. @@ -266,6 +368,7 @@ alert(color); // #FF7F00 Benefici: +<<<<<<< HEAD - `COLOR_ORANGE` è più facile da ricordare di `"#FF7F00"`. - E' più facile commettere errori scrivendo `"#FF7F00"` piuttosto che `COLOR_ORANGE`. - Quando leggiamo il codice, `COLOR_ORANGE` è molto più significativo di `#FF7F00`. @@ -273,6 +376,15 @@ Benefici: Quando dovremmo utilizzare lettere maiuscole per una costante, e quando invece trattarle come normali variabili? Facciamo un pò di chiarezza. Essere una "costante" significa che il valore non potrà mai cambiare. Ci sono costanti che sono note prima dell'esecuzione (come la codifia esadecimale del colore rosso), e ci sono quelle che vengono *calcolate* durante l'esecuzione, ma non cambieranno più dopo che gli sarà stato assegnato un valore. +======= +- `COLOR_ORANGE` is much easier to remember than `"#FF7F00"`. +- It is much easier to mistype `"#FF7F00"` than `COLOR_ORANGE`. +- When reading the code, `COLOR_ORANGE` is much more meaningful than `#FF7F00`. + +When should we use capitals for a constant and when should we name it normally? Let's make that clear. + +Being a "constant" just means that a variable's value never changes. But there are constants that are known prior to execution (like a hexadecimal value for red) and there are constants that are *calculated* in run-time, during the execution, but do not change after their initial assignment. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Per esempio: ```js @@ -280,12 +392,19 @@ const pageLoadTime = /* time taken by a webpage to load */; ``` Il valore di `pageLoadTime` non è noto prima del caricamento della pagina, quindi viene trattato come una normale variabile. Ma rimane comunque una costante, perchè non potrà più cambiare dopo che gli sarà stato assegnato un valore. +<<<<<<< HEAD In altre parole, i nomi delle costanti in maiuscolo vengono utilizzati con variabili dal valore noto prima dell'esecuzione. +======= +The value of `pageLoadTime` is not known prior to the page load, so it's named normally. But it's still a constant because it doesn't change after assignment. + +In other words, capital-named constants are only used as aliases for "hard-coded" values. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Dare i giusti nomi alle cose Parlando di variabili, c'è un'altra cosa estremamente importante. +<<<<<<< HEAD Per favore, utilizzate i nomi delle variabili con sensibilità. Prendetevi del tempo per pensare se necessario. Dare i giusti nomi alle variabili è una delle abilità più importanti (e difficili) nella programmazione. @@ -294,9 +413,19 @@ Una rapida occhiata ai nomi delle variabili può rivelare se il codice è stato In un progetto reale, la maggior parte del tempo lo si perde a modificare ed estendere del codice già esistente, piuttosto che riscriverne uno nuovo. E quando ritorneremo sul codice, dopo aver fatto qualcos'altro, sarà molto pù facile trovare informazioni se sono ben descritte. In altre parole, quando le variabili utilizzano dei nomi efficaci. Quindi è utile spendere del tempo a pensare il giusto nome per una variabile, prima di dichiararla. Questo approccio vi ripagherà. +======= +Please name your variables sensibly. Take time to think about this. + +Variable naming is one of the most important and complex skills in programming. A quick glance at variable names can reveal which code was written by a beginner versus an experienced developer. + +In a real project, most of the time is spent modifying and extending an existing code base rather than writing something completely separate from scratch. When we return to some code after doing something else for a while, it's much easier to find information that is well-labeled. Or, in other words, when the variables have good names. + +Please spend time thinking about the right name for a variable before declaring it. Doing so will repay you handsomely. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Alcune regole da seguire: +<<<<<<< HEAD - Utilizzare nomi leggibili da persone, come `userName` o `shoppingCart`. - Evitate abbreviazioni o nomi brevi come `a`, `b`, `c`, senza che abbiano veramente senso. - Rendete il nome il più descrittivo e preciso possibile. Esempi di pessimi nomi sono `data` e `value`. Questo tipo di nomi non dicono niente. Si possono utilizzare eccezionalmente se il contesto rende esplicito il significato. @@ -310,18 +439,45 @@ Come ultima cosa. Ci sono alcuni programmatori un pò pigri, che invece di dichi Il risultato che si ottiene, è che le variabili sono come delle scatole in cui si possono mettere varie cose, senza cambiare l'etichetta. Cosa ci sarà dentro in un dato momento? Chi lo sa... Siamo costretti a controllare manualmente. Questi programmatori risparmiano qualche bit nella dichiarazione delle variabili ma perdono dieci volte il tempo risparmiato per fare debugging del codice. +======= +- Use human-readable names like `userName` or `shoppingCart`. +- Stay away from abbreviations or short names like `a`, `b`, `c`, unless you really know what you're doing. +- Make names maximally descriptive and concise. Examples of bad names are `data` and `value`. Such names say nothing. It's only okay to use them if the context of the code makes it exceptionally obvious which data or value the variable is referencing. +- Agree on terms within your team and in your own mind. If a site visitor is called a "user" then we should name related variables `currentUser` or `newUser` instead of `currentVisitor` or `newManInTown`. + +Sounds simple? Indeed it is, but creating descriptive and concise variable names in practice is not. Go for it. + +```smart header="Reuse or create?" +And the last note. There are some lazy programmers who, instead of declaring new variables, tend to reuse existing ones. + +As a result, their variables are like boxes into which people throw different things without changing their stickers. What's inside the box now? Who knows? We need to come closer and check. + +Such programmers save a little bit on variable declaration but lose ten times more on debugging. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Una variable in più va bene, non sono il diavolo. +<<<<<<< HEAD I browser moderni e JavaScript minimizzano ed ottimizzano il codice abbastanza bene, quindi non ci saranno problemi di performance. Usare variabili differenti, per valori differenti può addirittura aiutare il motore JavaScript nell'ottimizzazione. +======= +Modern JavaScript minifiers and browsers optimize code well enough, so it won't create performance issues. Using different variables for different values can even help the engine optimize your code. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ``` ## Riepilogo +<<<<<<< HEAD Possiamo dichiarare variabili per memorizzare dati. Possono essere dichiarate con `var`,`let` o `const`. +======= +We can declare variables to store data by using the `var`, `let`, or `const` keywords. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb - `let` -- è una dichiarazione delle variabili più moderna. Il codice deve essere in modalità strict per utilizzare `let` in Chrome (V8). - `var` -- è una dichiarazione delle variabili più vecchia-scuola. Normalmente non si dovrebbe utilizzare, spiegheremo le sottili differenze da `let` nel capitolo , giusto per esserne a conoscenza. - `const` -- è simile a `let`, ma non consente di cambiare il valore della variabile. +<<<<<<< HEAD Le variabili dovrebbero avere dei nomi che ci consentono di capire facilmente cosa c'è dentro. +======= +Variables should be named in a way that allows us to easily understand what's inside them. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/04-variables/variable-change.png b/1-js/02-first-steps/04-variables/variable-change.png index 6dd3803d9..9135e930a 100644 Binary files a/1-js/02-first-steps/04-variables/variable-change.png and b/1-js/02-first-steps/04-variables/variable-change.png differ diff --git a/1-js/02-first-steps/04-variables/variable-change@2x.png b/1-js/02-first-steps/04-variables/variable-change@2x.png index f57b04ab1..c9569e638 100644 Binary files a/1-js/02-first-steps/04-variables/variable-change@2x.png and b/1-js/02-first-steps/04-variables/variable-change@2x.png differ diff --git a/1-js/02-first-steps/04-variables/variable.png b/1-js/02-first-steps/04-variables/variable.png index ab532d91d..6d2482556 100644 Binary files a/1-js/02-first-steps/04-variables/variable.png and b/1-js/02-first-steps/04-variables/variable.png differ diff --git a/1-js/02-first-steps/04-variables/variable@2x.png b/1-js/02-first-steps/04-variables/variable@2x.png index c9c37f034..845f34408 100644 Binary files a/1-js/02-first-steps/04-variables/variable@2x.png and b/1-js/02-first-steps/04-variables/variable@2x.png differ diff --git a/1-js/02-first-steps/05-types/article.md b/1-js/02-first-steps/05-types/article.md index d7b7d7333..472cee390 100644 --- a/1-js/02-first-steps/05-types/article.md +++ b/1-js/02-first-steps/05-types/article.md @@ -1,6 +1,10 @@ # Tipi di dato +<<<<<<< HEAD Una variabile in JavaScript può contenere qualsiasi dato. Una variabile può essere di tipo stringa in un istante e successivamente ricevere un valore numerico: +======= +A variable in JavaScript can contain any data. A variable can at one moment be a string and at another be a number: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js // no error @@ -10,7 +14,11 @@ message = 123456; I linguaggi di programmazione che lo consentono sono detti "dinamicamente tipati", questo significa che ci sono tipi di dato, ma le variabili non sono legate ad un tipo. +<<<<<<< HEAD Ci sono sette tipi di dato in JavaScript. Qui ne studiamo le basi, nel prossimo capitolo lo entreremo nei dettagli. +======= +There are seven basic data types in JavaScript. Here, we'll cover them in general and in the next chapters we'll talk about each of them in detail. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Tipo Numerico @@ -19,11 +27,19 @@ let n = 123; n = 12.345; ``` +<<<<<<< HEAD Il tipo *numerico* serve sia per i numeri interi che per quelli in virgola mobile. Ci sono varie operazioni disponibili con i numeri, ad esempio la moltiplicazione `*`, la divisione `/`, l'addizione `+`, la sottrazione `-` e molte altre. Oltre i normali numeri, ci sono anche i "valori numerici speciali" che appartengono sempre al tipo numerico: `Infinito`, `-Infinito` e `NaN`. +======= +The *number* type represents both integer and floating point numbers. + +There are many operations for numbers, e.g. multiplication `*`, division `/`, addition `+`, subtraction `-`, and so on. + +Besides regular numbers, there are so-called "special numeric values" which also belong to this data type: `Infinity`, `-Infinity` and `NaN`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb - `Infinito` rappresenta il concetto matematico [Infinito](https://en.wikipedia.org/wiki/Infinity) ∞. E' un valore speciale che è più grande di qualsiasi altro numero. @@ -33,7 +49,11 @@ Oltre i normali numeri, ci sono anche i "valori numerici speciali" che apparteng alert( 1 / 0 ); // Infinity ``` +<<<<<<< HEAD O inserendolo direttamente nel codice: +======= + Or just reference it directly: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run alert( Infinity ); // Infinity @@ -44,12 +64,17 @@ Oltre i normali numeri, ci sono anche i "valori numerici speciali" che apparteng alert( "not a number" / 2 ); // NaN, such division is erroneous ``` +<<<<<<< HEAD `NaN` è appiccicoso. Qualsiasi operazione su `NaN` restituirà `NaN`: +======= + `NaN` is sticky. Any further operation on `NaN` returns `NaN`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run alert( "not a number" / 2 + 5 ); // NaN ``` +<<<<<<< HEAD Quindi, se è presente un `NaN` da qualche parte nell'operazione matematica, questo si propagherà fino al risultato. ```smart header="Le operazioni matematiche sono sicure" @@ -59,12 +84,27 @@ Lo script non si interromperà mai con un errore fatale ("die"). Nel peggiore de ``` I numeri con valore speciale appartengono formalmente al tipo "numerico". Ovviamente non sono numeri nel vero senso della parola. +======= + So, if there's a `NaN` somewhere in a mathematical expression, it propagates to the whole result. + +```smart header="Mathematical operations are safe" +Doing maths is "safe" in JavaScript. We can do anything: divide by zero, treat non-numeric strings as numbers, etc. + +The script will never stop with a fatal error ("die"). At worst, we'll get `NaN` as the result. +``` + +Special numeric values formally belong to the "number" type. Of course they are not numbers in the common sense of this word. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Vedremo di più su come lavorare con i numeri nel capitolo . ## Tipo Stringa +<<<<<<< HEAD Una stringa in JavaScript deve essere tra apici. +======= +A string in JavaScript must be surrounded by quotes. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js let str = "Hello"; @@ -92,9 +132,15 @@ alert( `Hello, *!*${name}*/!*!` ); // Hello, John! alert( `the result is *!*${1 + 2}*/!*` ); // the result is 3 ``` +<<<<<<< HEAD L'espressione all'interno di `${…}` viene valutata ed il risultato diventa parte della stringa. Possiamo metterci qualsiasi cosa: una variabile come `name` oppure un espressione aritmetica come `1 + 2` o qualcosa di più complesso. Nota che questo è possibile sono tramite l'accento grave. Gli altri apici non lo consentono! +======= +The expression inside `${…}` is evaluated and the result becomes a part of the string. We can put anything in there: a variable like `name` or an arithmetical expression like `1 + 2` or something more complex. + +Please note that this can only be done in backticks. Other quotes don't have this embedding functionality! +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run alert( "the result is ${1 + 2}" ); // the result is ${1 + 2} (double quotes do nothing) ``` @@ -128,31 +174,57 @@ let isGreater = 4 > 1; alert( isGreater ); // true (the comparison result is "yes") ``` +<<<<<<< HEAD Copriremo i valori booleani più in dettaglio nel capitolo . +======= +We'll cover booleans more deeply in the chapter . +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Il valore "null" +<<<<<<< HEAD Il valore speciale `null` non appartiene a nessun tipo di quelli descritti fino ad ora. Fa parte di un altro tipo, che contiene solo il valore `null`: +======= +The special `null` value does not belong to any of the types described above. + +It forms a separate type of its own which contains only the `null` value: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js let age = null; ``` +<<<<<<< HEAD In JavaScript `null` non è un "riferimento ad un oggetto inesistente" o un "puntatore nullo" come in altri linguaggi. E' solamente un valore speciale che ha il senso di "nulla", "vuoto" o "valore sconosciuto". Il codice sopra indica che `age` è sconosciuto o vuoto per qualche motivo. +======= +In JavaScript, `null` is not a "reference to a non-existing object" or a "null pointer" like in some other languages. + +It's just a special value which represents "nothing", "empty" or "value unknown". + +The code above states that `age` is unknown or empty for some reason. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Il valore "undefined" +<<<<<<< HEAD Il valore speciale `undefined` vive a parte. Fa da tipo a se stesso, proprio come `null`. +======= +The special value `undefined` also stands apart. It makes a type of its own, just like `null`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Il significato di `undefined` è che "il valore non è assegnato". +<<<<<<< HEAD Se una variabile viene dichiarata, ma non assegnata, il suo valore è esattamente `undefined`: +======= +If a variable is declared, but not assigned, then its value is `undefined`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let x; @@ -170,26 +242,47 @@ x = undefined; alert(x); // "undefined" ``` +<<<<<<< HEAD ...Ma non è comunque consigliabile farlo. Normalmente, si utilizza `null` per descrivere un valore "vuoto" o "sconosciuto" della variabile, e `undefined` viene utilizzato solo per i controlli, per verificare se la variabile è stata assegnata. +======= +...But we don't recommend doing that. Normally, we use `null` to assign an "empty" or "unknown" value to a variable, and we use `undefined` for checks like seeing if a variable has been assigned. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Oggetti e Simboli Il tipo `object` è speciale. +<<<<<<< HEAD Tutti gli altri tipi descritti sono chiamati "primitivi", perchè i loro valori possono contenere solo una cosa (può essere una stringa, un numero o altro). Invece, gli oggetti vengono utilizzati per memorizzare una collezione di dati ed entità più complesse. Li tratteremo nel capitolo dopo avere appreso abbastanza sui tipi primitivi. Il tipo `symbol` viene utilizzato per creare identificatori unici per gli oggetti. Li abbiamo citati per completezza, ma è meglio studiarli dopo aver compreso gli oggetti. +======= +All other types are called "primitive" because their values can contain only a single thing (be it a string or a number or whatever). In contrast, objects are used to store collections of data and more complex entities. We'll deal with them later in the chapter after we learn more about primitives. + +The `symbol` type is used to create unique identifiers for objects. We have to mention it here for completeness, but it's better to study this type after objects. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## L'operatore typeof [#type-typeof] +<<<<<<< HEAD L'operatore `typeof` ritorna il tipo dell'argomento. E' utile quando vogliamo lavorare con valori di tipi differenti, o per eseguire controlli rapidi. +======= +The `typeof` operator returns the type of the argument. It's useful when we want to process values of different types differently or just want to do a quick check. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Sono supportate due sintassi: +<<<<<<< HEAD 1. Come operatore: `typeof x`. 2. Come funzione: `typeof(x)`. In altre parole, funziona sia con le parentesi che senza. Il risultato è lo stesso. +======= +1. As an operator: `typeof x`. +2. As a function: `typeof(x)`. + +In other words, it works with parentheses or without them. The result is the same. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Una chiamata a `typeof x` ritorna una stringa con il nome del tipo: @@ -217,11 +310,19 @@ typeof alert // "function" (3) */!* ``` +<<<<<<< HEAD Le ultime tre linee potrebbero richiedere una spiegazione ulteriore: 1. `Math` è un oggetto integrato che fornisce operazioni matematiche avanzate. Lo studieremo nel capitolo . Qui ha il semplice scopo di rappresentare un oggetto. 2. Il risultato di `typerisulaof null` è `"object"`. Questo è errato. E' un errore ufficialmente riconosciuto del `typeof`, mantenuto per retro-compatibilità. Ovviamente, `null` non è un oggetto. E' un valore speciale che fa da tipo a se stesso. Quindi, nuovamente, questo è un errore del linguaggio. 3. Il risultato di `typeof alert` è `"function"`, perchè `alert` è una funzione del linguaggio. Studieremo le funzioni nel prossimo capitolo, e vedremo che non c'e nessun tipo "funzione" nel linguaggio. Le funzioni appartengono al tipo oggetto. Ma `typeof` le tratta differentemente. Formalmente, è errato, ma molto utile nella pratica. +======= +The last three lines may need additional explanation: + +1. `Math` is a built-in object that provides mathematical operations. We will learn it in the chapter . Here, it serves just as an example of an object. +2. The result of `typeof null` is `"object"`. That's wrong. It is an officially recognized error in `typeof`, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own. So, again, this is an error in the language. +3. The result of `typeof alert` is `"function"`, because `alert` is a function of the language. We'll study functions in the next chapters where we'll see that there's no special "function" type in JavaScript. Functions belong to the object type. But `typeof` treats them differently. Formally, it's incorrect, but very convenient in practice. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Riepilogo @@ -237,8 +338,18 @@ Ci sono 7 tipi base in JavaScript. L'operatore `typeof` ci consente di vedere quale tipo è memorizzato nella variabile. +<<<<<<< HEAD - Due forme: `typeof x` o `typeof(x)`. - Ritorna una stringa con il nome del tipo, come `"string"`. - Il valore `null` ritorna `"object"` -- è un errore del linguaggio, infatti non è un oggetto. Nel prossimo capitolo ci concentreremo nei tipi primitivi e quando avremo preso familiarità, passeremo agli oggetti. +======= +The `typeof` operator allows us to see which type is stored in a variable. + +- Two forms: `typeof x` or `typeof(x)`. +- Returns a string with the name of the type, like `"string"`. +- For `null` returns `"object"` -- this is an error in the language, it's not actually an object. + +In the next chapters, we'll concentrate on primitive values and once we're familiar with them, we'll move on to objects. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md b/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md index 76063a438..fe083a7aa 100644 --- a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md +++ b/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md @@ -10,13 +10,22 @@ true + false = 1 "4" - 2 = 2 "4px" - 2 = NaN 7 / 0 = Infinity -" -9\n" + 5 = " -9\n5" -" -9\n" - 5 = -14 -null + 1 = 1 // (3) -undefined + 1 = NaN // (4) +" -9 " + 5 = " -9 5" // (3) +" -9 " - 5 = -14 // (4) +null + 1 = 1 // (5) +undefined + 1 = NaN // (6) ``` +<<<<<<< HEAD 1. L'addizione con una stringa di `"" + 1` converte `1` a stringa: `"" + 1 = "1"`, successivamente in `"1" + 0`, viene applicatala stessa regola. 2. La sottrazione `-` (come la maggior parte delle operazioni matematiche) funzionano solo con i numeri, converte quindi una stringa vuota `""` in `0`. 3. `null` diventa `0` dopo la conversione numerica. 4. `undefined` diventa `NaN` dopo la conversione numerica. +======= +1. The addition with a string `"" + 1` converts `1` to a string: `"" + 1 = "1"`, and then we have `"1" + 0`, the same rule is applied. +2. The subtraction `-` (like most math operations) only works with numbers, it converts an empty string `""` to `0`. +3. The addition with a string appends the number `5` to the string. +4. The subtraction always converts to numbers, so it makes `" -9 "` a number `-9` (ignoring spaces around it). +5. `null` becomes `0` after the numeric conversion. +6. `undefined` becomes `NaN` after the numeric conversion. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/task.md b/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/task.md index 9dae99edd..bc183f2a4 100644 --- a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/task.md +++ b/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/task.md @@ -17,8 +17,8 @@ true + false "4" - 2 "4px" - 2 7 / 0 -" -9\n" + 5 -" -9\n" - 5 +" -9 " + 5 +" -9 " - 5 null + 1 undefined + 1 ``` diff --git a/1-js/02-first-steps/06-type-conversions/article.md b/1-js/02-first-steps/06-type-conversions/article.md index 88b71fd90..5946a637f 100644 --- a/1-js/02-first-steps/06-type-conversions/article.md +++ b/1-js/02-first-steps/06-type-conversions/article.md @@ -1,13 +1,24 @@ # Conversione di tipi +<<<<<<< HEAD Nella maggior parte dei casi, operatori e funzioni convertono automaticamente il valore nel tipo corretto. Questo viene detto "conversione di tipi". +======= +Most of the time, operators and functions automatically convert the values given to them to the right type. This is called "type conversion". +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio, `alert` converte automaticamente un valore qualsiasi in una stringa, per poterla mostrare. Le operazioni matematica convertono i valori in numeri. +<<<<<<< HEAD Ci sono anche casi in cui è necessario convertire esplicitamente i valori per poter non provocare errori. ```smart header="Non parliamo ancora di oggetti" In questo capitolo non parleremo ancora di oggetti. Ci dedicheremo ai tipi primitivi. Successivamente, dopo aver capito gli oggetti, capire come funziona la conversione di oggetti, nel capitolo . +======= +There are also cases when we need to explicitly convert a value to the expected type. + +```smart header="Not talking about objects yet" +In this chapter, we won't cover objects. Instead, we'll study primitives first. Later, after we learn about objects, we'll see how object conversion works in the chapter . +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ``` ## ToString @@ -16,7 +27,11 @@ La conversione in stringa è utile quando abbiamo bisogno del formato stringa di Ad esempio, `alert(value)` effettua questa conversione per mostrare il valore. +<<<<<<< HEAD Possiamo anche utilizzare la funzione `String(value)`, per ottenere un risultato simile: +======= +We can also call the `String(value)` function to convert a value to a string: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let value = true; @@ -28,7 +43,11 @@ alert(typeof value); // string */!* ``` +<<<<<<< HEAD La conversione in stringa è quella più ovvia. Il valore `false` diventa la stringa `"false"`, mentre `null` diventa `"null"` etc. +======= +String conversion is mostly obvious. A `false` becomes `"false"`, `null` becomes `"null"`, etc. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## ToNumber @@ -39,8 +58,13 @@ Ad esempio, quando la divisione `/` viene applicata ad un tipo non numerico: ```js run alert( "6" / "2" ); // 3, strings are converted to numbers ``` +<<<<<<< HEAD funzine Possiamo utilizzare la funzione `Number(value)` per convertire esplicitamente un valore `value`: +======= + +We can use the `Number(value)` function to explicitly convert a `value` to a number: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let str = "123"; @@ -51,9 +75,15 @@ let num = Number(str); // becomes a number 123 alert(typeof num); // number ``` +<<<<<<< HEAD Una conversione esplicita è solitamente richiesta quando leggiamo un valore da una risorsa di tipo stringa, come un form testuale, ma ci aspettiamo l'inserimento di un numero. Se la stringa non risulta essere un numero valido, il risultato della conversione sarà `NaN`, ad esempio: +======= +Explicit conversion is usually required when we read a value from a string-based source like a text form but expect a number to be entered. + +If the string is not a valid number, the result of such a conversion is `NaN`. For instance: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let age = Number("an arbitrary string instead of a number"); @@ -67,8 +97,13 @@ Le regole di conversione numerica: |-------|-------------| |`undefined`|`NaN`| |`null`|`0`| +<<<<<<< HEAD |true e false | `1` e `0` | | `string` | Gli spazi bianchi dall'inizio e dalla fine vengono rimossi. Poi, se il resto della stringa è vuota, il risultato è `0`. Altrimenti, il numero viene "letto" dalla stringa. Un errore restituirà `NaN`. | +======= +|true and false | `1` and `0` | +| `string` | Whitespaces from the start and end are removed. If the remaining string is empty, the result is `0`. Otherwise, the number is "read" from the string. An error gives `NaN`. | +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Esempi: @@ -79,31 +114,53 @@ alert( Number(true) ); // 1 alert( Number(false) ); // 0 ``` +<<<<<<< HEAD Nota che `null` e `undefined` si comportano diversamente: `null` diventa zero, mentre `undefined` diventa `NaN`. ````smart header="L'addizione '+' concatena le stringhe" Quasi tutte le operazioni matematiche convertono valori in numeri. Con un importante eccezione per l'addizione `+`. Se uno degli operandi è una stringa, allora anche gli altri vengono convertiti in stringhe. E successivamente li concatena (unisce): +======= +Please note that `null` and `undefined` behave differently here: `null` becomes zero while `undefined` becomes `NaN`. + +````smart header="Addition '+' concatenates strings" +Almost all mathematical operations convert values to numbers. A notable exception is addition `+`. If one of the added values is a string, the other one is also converted to a string. + +Then, it concatenates (joins) them: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run alert( 1 + '2' ); // '12' (string to the right) alert( '1' + 2 ); // '12' (string to the left) ``` +<<<<<<< HEAD Questo accade solo quando almeno uno degli argomenti è di tipo stringa. Altrimenti, i valori vengono convertiti in numeri +======= +This only happens when at least one of the arguments is a string. Otherwise, values are converted to numbers. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```` ## ToBoolean La conversione booleana è quella più semplice. +<<<<<<< HEAD Questa si verifica con le operazioni logiche (più avanti incontreremo i testi di condizione ed altri tipi di operazione logiche), ma può anche essere richiamato manualmente con la funzione `Boolean(value)`. +======= +It happens in logical operations (later we'll meet condition tests and other similar things) but can also be performed explicitly with a call to `Boolean(value)`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Le regole di conversione: +<<<<<<< HEAD - I valori che sono intuitivamente "vuoti", come lo `0`, una stringa vuota, `null`, `undefined` e `NaN` diventano `false`. - Gli altri valori diventano `true`. +======= +- Values that are intuitively "empty", like `0`, an empty string, `null`, `undefined`, and `NaN`, become `false`. +- Other values become `true`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -115,8 +172,13 @@ alert( Boolean("hello") ); // true alert( Boolean("") ); // false ``` +<<<<<<< HEAD ````warn header="Da notare: una stringa contenente `\"0\"` viene valutata come `true`" Alcun linguaggi (come il PHP) trattano `"0"` come `false`. Diversamente in JavaScript una stringa non vuota è sempre `true`. +======= +````warn header="Please note: the string with zero `\"0\"` is `true`" +Some languages (namely PHP) treat `"0"` as `false`. But in JavaScript, a non-empty string is always `true`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run alert( Boolean("0") ); // true @@ -127,11 +189,19 @@ alert( Boolean(" ") ); // spaces, also true (any non-empty string is true) ## Riepilogo +<<<<<<< HEAD I tre tipi di conversioni più utilizzati sono: a *string*, a *number* e a *boolean*. **`ToString`** -- Avviene quando stampiamo qualcosa a schermo, può essere richiamato con `String(value)`. La conversione a stringa è solitamente ovvia per i valore primitivi. **`ToNumber`** -- Utilizzata nelle operazioni matematiche, può essere richiamata esplicitamente con `Number(value)`. +======= +The three most widely used type conversions are to string, to number, and to boolean. + +**`ToString`** -- Occurs when we output something. Can be performed with `String(value)`. The conversion to string is usually obvious for primitive values. + +**`ToNumber`** -- Occurs in math operations. Can be performed with `Number(value)`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb La conversione segue le seguenti regole: @@ -142,7 +212,11 @@ La conversione segue le seguenti regole: |true / false | `1 / 0` | | `string` | La stringa viene letta per "com'è", gli spazi bianchi agli estremi vengono ignorati. Una stringa vuota diventa `0`. Un errore restituisce `NaN`. | +<<<<<<< HEAD **`ToBoolean`** -- Avviene nelle operazioni logiche, può anche essere richiamato esplicitamente con `Boolean(value)`. +======= +**`ToBoolean`** -- Occurs in logical operations. Can be performed with `Boolean(value)`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Segue le regole: @@ -157,4 +231,8 @@ La maggior parte di queste regole sono facili da capire e memorizzare. Gli error - `undefined` vale `NaN` come un numero, non `0`. - `"0"` e le stringhe che contengono solamente spazi `" "` vengono interpretate come true. +<<<<<<< HEAD Qui non abbiamo coperto gli oggetti, ci ritorneremo più avanti nel capitolo che è dedicato esclusivamente agli oggetti, dopo che avremmo imparato più cose basilari su JavaScript. +======= +Objects aren't covered here. We'll return to them later in the chapter that is devoted exclusively to objects after we learn more basic things about JavaScript. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/07-operators/article.md b/1-js/02-first-steps/07-operators/article.md index 592857f91..cc9ffe082 100644 --- a/1-js/02-first-steps/07-operators/article.md +++ b/1-js/02-first-steps/07-operators/article.md @@ -1,15 +1,28 @@ # Operatori +<<<<<<< HEAD Molti operatori già li conosciamo dalle scuole. Tra cui l'addizione `+`, la moltiplicazione `*`, la sottrazione `-` e molti altri. In questo capitolo ci concentreremo sugli aspetti che non vengono descritti a scuola. +======= +We know many operators from school. They are things like addition `+`, multiplication `*`, subtraction `-`, and so on. + +In this chapter, we'll concentrate on aspects of operators that are not covered by school arithmetic. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Termini: "unario", "binario", "operando" +<<<<<<< HEAD Prima di procedere, cerchiamo di capire la terminologia. - *Un operando* -- è quello su cui si applica l'operatore. Ad esempio nella moltiplicazione `5 * 2` ci sono due operandi: l'operando sinistro cioè il `5`, e l'operando di destra cioè il `2`. Qualche volta le persone dicono "argomenti" piuttosto che "operandi". - Un operatore è *unario* se ha un singolo operando. Ad esempio, la negazione `-` inverte il segno di un numero: +======= +Before we move on, let's grasp some common terminology. + +- *An operand* -- is what operators are applied to. For instance, in the multiplication of `5 * 2` there are two operands: the left operand is `5` and the right operand is `2`. Sometimes, people call these "arguments" instead of "operands". +- An operator is *unary* if it has a single operand. For example, the unary negation `-` reverses the sign of a number: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let x = 1; @@ -19,13 +32,18 @@ Prima di procedere, cerchiamo di capire la terminologia. */!* alert( x ); // -1, unary negation was applied ``` +<<<<<<< HEAD - Un operatore è *binario* se ha due operandi. Lo stesso operatore "meno" esiste nella forma binaria: +======= +- An operator is *binary* if it has two operands. The same minus exists in binary form as well: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run no-beautify let x = 1, y = 3; alert( y - x ); // 2, binary minus subtracts values ``` +<<<<<<< HEAD Formalmente, stiamo parlando di due operatori diversi: la negazione unaria (un singolo operando, inverte il segno) e la sottrazione binaria (due operandi, si esegue la sottrazione). ## Concatenazione di stringhe, operatore binario + @@ -35,13 +53,28 @@ Adesso diamo un'occhiata alle caratteristiche degli operatori in JavaScript, che L'operatore di somma `+` viene utilizzato per sommare due numeri. Ma se l'operatore binario `+` viene applicato a delle stringhe, queste verranno unite (concatenate): +======= + Formally, we're talking about two different operators here: the unary negation (single operand: reverses the sign) and the binary subtraction (two operands: subtracts). + +## String concatenation, binary + + +Now, let's see special features of JavaScript operators that are beyond school arithmetics. + +Usually, the plus operator `+` sums numbers. + +But, if the binary `+` is applied to strings, it merges (concatenates) them: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js let s = "my" + "string"; alert(s); // mystring ``` +<<<<<<< HEAD Nota che se almeno uno degli operandi è una stringa, anche gli altri verrano convertiti a stringa. +======= +Note that if one of the operands is a string, the other one is converted to a string too. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -50,7 +83,11 @@ alert( '1' + 2 ); // "12" alert( 2 + '1' ); // "21" ``` +<<<<<<< HEAD Come hai visto, non è importante se la stringa è il primo operando o il secondo. La regola è semplice: se uni degli operandi è una stringa, anche gli altri vengono convertiti a stringa. +======= +See, it doesn't matter whether the first operand is a string or the second one. The rule is simple: if either operand is a string, the other one is converted into a string as well. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Comunque, queste operazioni vengono eseguite da sinistra verso destra, Se ci sono due numeri prima di una stringa, prima vengono sommati e il risultato convertito in stringa: @@ -59,7 +96,11 @@ Comunque, queste operazioni vengono eseguite da sinistra verso destra, Se ci son alert(2 + 2 + '1' ); // "41" and not "221" ``` +<<<<<<< HEAD La concatenazione di stringhe e la conversione è una caratteristica particolare dell'operatore binario `+`. Gli altri operatori aritmetici funzionano solamente con i numeri. Infatti convertono sempre i loro operandi a numeri. +======= +String concatenation and conversion is a special feature of the binary plus `+`. Other arithmetic operators work only with numbers and always convert their operands to numbers. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio, la sottrazione e la divisione: @@ -70,9 +111,15 @@ alert( '6' / '2' ); // 3 ## Conversione numerica, operatore unario + +<<<<<<< HEAD L'operatore somma `+` esiste in due forme. La forma binaria che abbiamo utilizzato sopra, e quella unaria. L'operatore unario di somma `+` viene applicato ad un singolo valore, non fa nulla con i numero, ma se l'operando non è un numero, viene convertito in un operando di tipo numerico. +======= +The plus `+` exists in two forms: the binary form that we used above and the unary form. + +The unary plus or, in other words, the plus operator `+` applied to a single value, doesn't do anything to numbers. But if the operand is not a number, the unary plus converts it into a number. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -91,9 +138,15 @@ alert( +"" ); // 0 */!* ``` +<<<<<<< HEAD Si ottiene lo stesso risultato di `Number(...)`, ma in un modo più veloce. La necessità di convertire stringhe in numeri si presenta molto spesso. Ad esempio, se stiamo prelevando un valore da un campo HTML, questo solitamente sarà di tipo stringa. +======= +It actually does the same thing as `Number(...)`, but is shorter. + +The need to convert strings to numbers arises very often. For example, if we are getting values from HTML form fields, they are usually strings. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Come procedere in caso volessimo sommare questi valori? @@ -106,7 +159,11 @@ let oranges = "3"; alert( apples + oranges ); // "23", the binary plus concatenates strings ``` +<<<<<<< HEAD Se invece vogliamo trattarli come numeri, possiamo prima convertirli e successivamente sommarli: +======= +If we want to treat them as numbers, we need to convert and then sum them: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let apples = "2"; @@ -121,6 +178,7 @@ alert( +apples + +oranges ); // 5 // alert( Number(apples) + Number(oranges) ); // 5 ``` +<<<<<<< HEAD Dal punto di vista di matematico l'abbondanza di operatori di somma può risultare strano. Diversamente, dal punto di vista informatico, non c'e nulla di speciale: la somma unaria viene applicata per prima, si occupa di convertire stringhe in numeri, successivamente la somma binaria esegue l'addizione. Perchè la somma unaria viene applicata prima di quella binaria? Come adesso vedremo, questo accade per via della *precedenza maggiore*. @@ -136,6 +194,23 @@ Le parentesi, superano qualsiasi precedenza, quindi se non siamo soddisfatti del Ci sono molti operatori in JavaScript. Ogni operatore ha un suo grado di precedenza. Quello con il grado più elevato viene eseguito per primo. Se il grado di precedenza è uguale si esegue da sinistra a destra. Un estratto della [tabella delle precedenze](https://developer.mozilla.org/en/JavaScript/Reference/operators/operator_precedence) (non è necessario che ve la ricordiate, ma tenete a mente che gli operatori unari hanno una precedenza più elevata rispetto ai corrispondenti binari): +======= +From a mathematician's standpoint, the abundance of pluses may seem strange. But from a programmer's standpoint, there's nothing special: unary pluses are applied first, they convert strings to numbers, and then the binary plus sums them up. + +Why are unary pluses applied to values before the binary ones? As we're going to see, that's because of their *higher precedence*. + +## Operator precedence + +If an expression has more than one operator, the execution order is defined by their *precedence*, or, in other words, the implicit priority order of operators. + +From school, we all know that the multiplication in the expression `1 + 2 * 2` should be calculated before the addition. That's exactly the precedence thing. The multiplication is said to have *a higher precedence* than the addition. + +Parentheses override any precedence, so if we're not satisfied with the implicit order, we can use them to change it. For example: `(1 + 2) * 2`. + +There are many operators in JavaScript. Every operator has a corresponding precedence number. The one with the larger number executes first. If the precedence is the same, the execution order is from left to right. + +Here's an extract from the [precedence table](https://developer.mozilla.org/en/JavaScript/Reference/operators/operator_precedence) (you don't need to remember this, but note that unary operators are higher than corresponding binary ones): +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb | Precedence | Name | Sign | |------------|------|------| @@ -150,13 +225,21 @@ Un estratto della [tabella delle precedenze](https://developer.mozilla.org/en/Ja | 3 | assignment | `=` | | ... | ... | ... | +<<<<<<< HEAD Come possiamo vedere, la "somma unaria"(unary plus) ha una priorità di `16`, che è maggiore di `13` dell'addizione(somma binaria). Questo è il motivo per cui l'espressione `"+apples + +oranges"` esegue prima la somma unaria, e successivamente l'addizione. +======= +As we can see, the "unary plus" has a priority of `16` which is higher than the `13` of "addition" (binary plus). That's why, in the expression `"+apples + +oranges"`, unary pluses work before the addition. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Assegnazione Da notare che anche l'assegnazione `=` è un operatore. Viene infatti elencato nella tabella delle precedenze con una priorità molto bassa: `3`. +<<<<<<< HEAD Questo è il motivo per cui quando assegniamo un valore ad una variabile, come `x = 2 * 2 + 1`, i calcoli vengono eseguiti per primi, e successivamente viene valutato l'operatore `=`, che memorizza il risultato in `x`. +======= +That's why, when we assign a variable, like `x = 2 * 2 + 1`, the calculations are done first and then the `=` is evaluated, storing the result in `x`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js let x = 2 * 2 + 1; @@ -178,14 +261,25 @@ alert( b ); // 4 alert( c ); // 4 ``` +<<<<<<< HEAD Le assegnazioni concatenate vengono valutate da destra a sinistra. Prima viene valutata l'espressione più a destra `2 + 2` e successivamente viene valutata l'assegnazione a sinistra: `c`, `b` e `a`. Alla fine, tutte le variabili condivideranno lo stesso valore. ````smart header="L'operatore di assegnazione`\"=\"` ritorna un valore" Un operatore ritorna sempre un valore. Questo è ovvio per molti operatori come l'addizione `+` o la moltiplicazione `*`. Ma anche l'operatore di assegnazione segue la stessa regola. +======= +Chained assignments evaluate from right to left. First, the rightmost expression `2 + 2` is evaluated and then assigned to the variables on the left: `c`, `b` and `a`. At the end, all the variables share a single value. + +````smart header="The assignment operator `\"=\"` returns a value" +An operator always returns a value. That's obvious for most of them like addition `+` or multiplication `*`. But the assignment operator follows this rule too. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb La chiamata `x = value` scrive `value` dentro `x` *e successivamente la ritorna*. +<<<<<<< HEAD Qui c'e una demo che usa un assegnazione come parte di un espressione più complessa: +======= +Here's a demo that uses an assignment as part of a more complex expression: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let a = 1; @@ -201,12 +295,20 @@ alert( c ); // 0 Nell'esempio qui sopra, il risultato di `(a = b + 1)` è il valore che viene assegnato ad `a` (che è `3`). Viene poi utilizzato per sottrarlo a `3`. +<<<<<<< HEAD Sembra un codice strano, no? Dovremmo quindi capire perchè e come funzione, poichè potrete incontrarlo in alcune librerie di terze parti, anche se non dovreste mai scrivere nulla di simile. Queste scorciatoie non rendono per niente il codice pulito e leggibile. +======= +Funny code, isn't it? We should understand how it works, because sometimes we see it in 3rd-party libraries, but shouldn't write anything like that ourselves. Such tricks definitely don't make code clearer or readable. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```` ## Resto % +<<<<<<< HEAD L'operatore di resto `%`, anche se può sembrare, non ha nulla a che fare con le percentuali. +======= +The remainder operator `%`, despite its appearance, is not related to percents. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Il risultato di `a % b` è il resto della divisione intera tra `a` e `b`. @@ -232,7 +334,13 @@ alert( 2 ** 3 ); // 8 (2 * 2 * 2) alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2) ``` +<<<<<<< HEAD L'operatore funziona anche con valori non interi di `a` e `b`, ad esempio: +======= +The operator works for non-integer numbers as well. + +For instance: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root, that's maths) @@ -245,7 +353,11 @@ alert( 8 ** (1/3) ); // 2 (power of 1/3 is the same as a cubic root) L'incremento o il decremento di un numero di uno è una delle operazioni numeriche più comuni. +<<<<<<< HEAD Quindi, c'è un operatore speciale per questo: +======= +So, there are special operators for it: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb - **Incremento** `++` incrementa la variabile di 1: @@ -263,6 +375,7 @@ Quindi, c'è un operatore speciale per questo: ``` ```warn +<<<<<<< HEAD L'Incremento/decremento possono essere applicati solo a variabili. Se tentiamo di utilizzarli con un valore, come `5++` verrà sollevato un errore. ``` @@ -272,12 +385,29 @@ Gli operatori `++` e `--` possono essere inseriti sia prima che dopo la variabil - La "forma pre-fissa" si ottiene inserendo l'operatore prima della variabile: `++counter`. Entrambi questi metodi portano allo stesso risultato: incrementano `counter` di `1`. +======= +Increment/decrement can only be applied to variables. Trying to use it on a value like `5++` will give an error. +``` + +The operators `++` and `--` can be placed either before or after a variable. + +- When the operator goes after the variable, it is in "postfix form": `counter++`. +- The "prefix form" is when the operator goes before the variable: `++counter`. + +Both of these statements do the same thing: increase `counter` by `1`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb C'è qualche differenza? Si, ma possiamo notarla solo se andiamo ad utilizzare il valore di ritorno di `++/--`. +<<<<<<< HEAD Facciamo chiarezza. Come sappiamo, tutti gli operatori ritornano un valore. L'incremento/decremento non fanno eccezione. La forma pre-fissa ritorna il nuovo valore, mentre la forma post-fissa ritorna il vecchio valore(prima dell'incremento/decremento). Per vedere le differenze, guardiamo un esempio: +======= +Let's clarify. As we know, all operators return a value. Increment/decrement is no exception. The prefix form returns the new value while the postfix form returns the old value (prior to increment/decrement). + +To see the difference, here's an example: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let counter = 1; @@ -286,9 +416,15 @@ let a = ++counter; // (*) alert(a); // *!*2*/!* ``` +<<<<<<< HEAD Nella riga `(*)` la chiamata pre-fissa di `++counter` incrementa `counter` e ritorna il nuovo valore che è `2`. Quindi `alert` mostra `2`. Adesso proviamo ad utilizzare la forma post-fissa: +======= +In the line `(*)`, the *prefix* form `++counter` increments `counter` and returns the new value, `2`. So, the `alert` shows `2`. + +Now, let's use the postfix form: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let counter = 1; @@ -297,11 +433,19 @@ let a = counter++; // (*) changed ++counter to counter++ alert(a); // *!*1*/!* ``` +<<<<<<< HEAD Nella riga `(*)` la forma *post-fissa* `counter++` incrementa `counter`, ma ritorna il *vecchio* valore(prima dell'incremento). Quindi `alert` mostra `1`. +======= +In the line `(*)`, the *postfix* form `counter++` also increments `counter` but returns the *old* value (prior to increment). So, the `alert` shows `1`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Per ricapitolare: +<<<<<<< HEAD - Se il risultato di un incremento/decremento non viene utilizzato, non ci sarà differenza qualsiasi forma venga utilizzata: +======= +- If the result of increment/decrement is not used, there is no difference in which form to use: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let counter = 0; @@ -309,21 +453,34 @@ Per ricapitolare: ++counter; alert( counter ); // 2, the lines above did the same ``` +<<<<<<< HEAD - Se l'intenzione è di incrementare il valore *e* utilizzare il valore, allora si utilizza la forma pre-fissa: +======= +- If we'd like to increase a value *and* immediately use the result of the operator, we need the prefix form: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let counter = 0; alert( ++counter ); // 1 ``` +<<<<<<< HEAD - Se si ha intenzione di incrementare, ma utilizzare il valore precedente, allora sarà necessario utilizzare la forma post-fissa: +======= +- If we'd like to increment a value but use its previous value, we need the postfix form: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let counter = 0; alert( counter++ ); // 0 ``` +<<<<<<< HEAD ````smart header="Incremento/decremento contro gli operatori" Gli operatori `++/--` possono essere utilizzati nello stesso modo all'interno di un espressione. La loro precedenza sarà maggiore degli altri operatori aritmetici. +======= +````smart header="Increment/decrement among other operators" +The operators `++/--` can be used inside expressions as well. Their precedence is higher than most other arithmetical operations. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -339,11 +496,19 @@ let counter = 1; alert( 2 * counter++ ); // 2, because counter++ returns the "old" value ``` +<<<<<<< HEAD Sebbene sia tecnicamente permesso, questa notazione rende il codice meno leggibile. Una linea che esegue più operazioni -- non è mai un bene. Mentre leggiamo il codice, una rapido scorrimento con lo sguardo in "verticale" può facilmente far saltare la lettura di un parte di codice, come ad esempio `counter++`, e potrebbe quindi non essere ovvio che la variabile incrementa. E' consigliato utilizzare lo stile "una linea -- un'azione": +======= +Though technically okay, such notation usually makes code less readable. One line does multiple things -- not good. + +While reading code, a fast "vertical" eye-scan can easily miss something like `counter++` and it won't be obvious that the variable increased. + +We advise a style of "one line -- one action": +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let counter = 1; @@ -368,11 +533,19 @@ La lista degli operatori: - RIGHT SHIFT ( `>>` ) - ZERO-FILL RIGHT SHIFT ( `>>>` ) +<<<<<<< HEAD Questi operatori vengono utilizzati raramente. Per capirli, dovremmo entrare nella rappresentazione dei numeri a basso livello, e non è questo il contesto per approfondirli. Specialmente perchè non ne avremmo bisogno presto. Se siete curiosi, potete leggere l'articolo sugli [operatori BitWise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) su MDN. Potrebbero essere molto utili in certe situazioni di necessità. +======= +These operators are used very rarely. To understand them, we need to delve into low-level number representation and it would not be optimal to do that right now, especially since we won't need them any time soon. If you're curious, you can read the [Bitwise Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) article on MDN. It would be more practical to do that when a real need arises. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Modifica-in-posizione +<<<<<<< HEAD Abbiamo spesso necessità di applicare un operatore ad una variabile e memorizzare il nuovo risultato. +======= +We often need to apply an operator to a variable and store the new result in that same variable. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -382,7 +555,11 @@ n = n + 5; n = n * 2; ``` +<<<<<<< HEAD Questa notazione può essere accorciata utilizzando gli operatori `+=` e `*=`: +======= +This notation can be shortened using the operators `+=` and `*=`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let n = 2; @@ -392,7 +569,11 @@ n *= 2; // now n = 14 (same as n = n * 2) alert( n ); // 14 ``` +<<<<<<< HEAD Gli operatori di abbreviazione "modifica-in-posizione" esistono per tutti gli operatori, anche per quelli bit a bit(bitwise): `/=`, `-=` etc. +======= +Short "modify-and-assign" operators exist for all arithmetical and bitwise operators: `/=`, `-=`, etc. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Questi operatori hanno la stessa precedenza di una normale assegnazione, quindi l'assegnazione verrà effettuata dopo gli altri calcoli: @@ -406,9 +587,15 @@ alert( n ); // 16 (right part evaluated first, same as n *= 8) ## Virgola +<<<<<<< HEAD L'operatore virgola `,` è uno degli operatori più rari ed inusuali. Qualche volta viene utilizzato per scrivere codice più breve, quindi è necessario capirlo bene per sapere cosa sta succedendo. L'operatore virgola ci consente di valutare diverse espressioni, dividendole con `,`. Ogni espressione viene valutata, ma solo dell'ultima viene ritornato il risultato. +======= +The comma operator `,` is one of the rarest and most unusual operators. Sometimes, it's used to write shorter code, so we need to know it in order to understand what's going on. + +The comma operator allows us to evaluate several expressions, dividing them with a comma `,`. Each of them is evaluated but only the result of the last one is returned. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -420,17 +607,30 @@ let a = (1 + 2, 3 + 4); alert( a ); // 7 (the result of 3 + 4) ``` +<<<<<<< HEAD Qui la prima espressione `1 + 2` viene valutata, ed il suo risultato viene scartato, successivamente viene eseguito `3 + 4` e il suo risultato viene ritornato. +======= +Here, the first expression `1 + 2` is evaluated and its result is thrown away. Then, `3 + 4` is evaluated and returned as the result. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```smart header="La virgola ha una precedenza molto bassa" L'operatore virgola ha una precedenza molto bassa, più bassa di `=`, quindi le parentesi sono importanti nel'esempio sopra. +<<<<<<< HEAD Senza parentesi: `a = 1 + 2, 3 + 4` verrebbe valutato `+` prima, sommando i numeri in `a = 3, 7`, poi viene valutato l'operatore di assegnazione `=` che assegna `a = 3`, e successivamente il numero `7` dopo la virgola, che viene ignorato. ``` Perchè dovremmo avere bisogno di un operatore che non ritorna nulla tranne l'ultima parte? Qualche volta le persone lo utilizzano in costrutti più complessi per eseguire più azioni in una sola riga. +======= +Without them: `a = 1 + 2, 3 + 4` evaluates `+` first, summing the numbers into `a = 3, 7`, then the assignment operator `=` assigns `a = 3`, and finally the number after the comma, `7`, is not processed so it's ignored. +``` + +Why do we need an operator that throws away everything except the last part? + +Sometimes, people use it in more complex constructs to put several actions in one line. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -441,4 +641,8 @@ for (*!*a = 1, b = 3, c = a * b*/!*; a < 10; a++) { } ``` +<<<<<<< HEAD Questo "trick" viene utilizzato in molti framework JavaScript, per questo l'abbiamo menzionato. Ma solitamente non migliora la leggibilità del codice, quindi dovremmo pensarci bene prima di scrivere questo tipo di espressioni. +======= +Such tricks are used in many JavaScript frameworks. That's why we're mentioning them. But, usually, they don't improve code readability so we should think well before using them. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/08-comparison/article.md b/1-js/02-first-steps/08-comparison/article.md index 7e110b6b0..ef1d2bb69 100644 --- a/1-js/02-first-steps/08-comparison/article.md +++ b/1-js/02-first-steps/08-comparison/article.md @@ -1,18 +1,34 @@ # Confronti +<<<<<<< HEAD Molti operatori di confronto li conosciamo già dalla matematica: - Maggiore/minore di: a > b, a < b. - Maggiore/minore o uguale di: a >= b, a <= b. - L'uguaglianza viene scritta come `a == b` (da notare che si utilizza due volte il simbolo `=`. Il simbolo unico`a = b` significa assegnazione). - Non uguale. In matematica la notazione è , in JavaScript viene scritto come un'assegnazione ma con un punto esclamativo davanti: a != b. +======= +We know many comparison operators from maths: + +- Greater/less than: a > b, a < b. +- Greater/less than or equals: a >= b, a <= b. +- Equals: `a == b` (please note the double equals sign `=`. A single symbol `a = b` would mean an assignment). +- Not equals. In maths the notation is , but in JavaScript it's written as an assignment with an exclamation sign before it: a != b. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Il risultato è booleano +<<<<<<< HEAD Come tutti gli altri operatori, quelli di confronto ritornano un valore. In questo caso il valore di ritorno è di tipo booleano. - `true` -- significa "si", "corretto" o "vero". - `false` -- significa "no", "sbagliato" o "falso". +======= +Like all other operators, a comparison returns a value. In this case, the value is a boolean. + +- `true` -- means "yes", "correct" or "the truth". +- `false` -- means "no", "wrong" or "not the truth". +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -31,7 +47,11 @@ alert( result ); // true ## Confronto di stringhe +<<<<<<< HEAD Per vedere quale stringa è più lunga di un'altra, viene utilizzato l'ordine "dizionario" o meglio "lessico-grafico". +======= +To see whether a string is greater than another, JavaScript uses the so-called "dictionary" or "lexicographical" order. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb In altre parole, le stringhe vengono confrontate lettera a lettera. @@ -45,6 +65,7 @@ alert( 'Bee' > 'Be' ); // true L'algoritmo per confrontare due stringhe è semplice: +<<<<<<< HEAD 1. Confronta i primi caratteri di entrambe le stringhe. 2. Se il primo è maggiore(o minore), allora la prima stringa è maggiore(o minore) della seconda. Abbiamo quindi finito. 3. Altrimenti se i primi caratteri sono uguali, vengono comparati i secondi, nella stessa maniera. @@ -54,20 +75,40 @@ L'algoritmo per confrontare due stringhe è semplice: Nell'esempio sopra, il confronto `'Z' > 'A'` ritorna il risultato al primo passo dell'algoritmo. Le stringhe `"Glow"` e `"Glee"` vengono confrontate carattere per carattere: +======= +1. Compare the first character of both strings. +2. If the first character from the first string is greater (or less) than the other string's, then the first string is greater (or less) than the second. We're done. +3. Otherwise, if both strings' first characters are the same, compare the second characters the same way. +4. Repeat until the end of either string. +5. If both strings end at the same length, then they are equal. Otherwise, the longer string is greater. + +In the examples above, the comparison `'Z' > 'A'` gets to a result at the first step while the strings `"Glow"` and `"Glee"` are compared character-by-character: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb 1. `G` è uguale a `G`. 2. `l` è uguale a `l`. 3. `o` è maggiore di `e`. Qui si ferma. La prima stringa è maggiore. +<<<<<<< HEAD ```smart header="Non un vero dizionario, ma un ordine Unicode" L'algoritmo di confronto esaminato sopra è molto simile a quello utilizzato nei dizionari cartacei o nelle agende telefoniche. Ma non è proprio uguale. Ad esempio, le lettere maiuscole contano. Una lettera maiuscola `"A"` non è uguale alla stessa minuscola `"a"`. Qual'è più grande? In realtà, quella minuscola. Come mai? Perchè le lettere minuscole hanno un indice di encoding maggiore nella tabella(Unicode). Ritorneremo nei dettagli specifici e alle conseguenze che porta nel capitolo . +======= +```smart header="Not a real dictionary, but Unicode order" +The comparison algorithm given above is roughly equivalent to the one used in dictionaries or phone books, but it's not exactly the same. + +For instance, case matters. A capital letter `"A"` is not equal to the lowercase `"a"`. Which one is greater? The lowercase `"a"`. Why? Because the lowercase character has a greater index in the internal encoding table JavaScript uses (Unicode). We'll get back to specific details and consequences of this in the chapter . +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ``` ## Confronti tra tipi diversi +<<<<<<< HEAD Quando compariamo valori che appartengono a tipi differenti, questi vengono convertiti in numeri. +======= +When comparing values of different types, JavaScript converts the values to numbers. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -76,7 +117,13 @@ alert( '2' > 1 ); // true, string '2' becomes a number 2 alert( '01' == 1 ); // true, string '01' becomes a number 1 ``` +<<<<<<< HEAD Per i valori booleani, `true` diventa `1` e `false` diventa `0`, quindi: +======= +For boolean values, `true` becomes `1` and `false` becomes `0`. + +For example: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run alert( true == 1 ); // true @@ -101,24 +148,40 @@ alert( Boolean(b) ); // true alert(a == b); // true! ``` +<<<<<<< HEAD Dal punto di vista di JavaScript questo è abbastanza normale. Un controllo di uguaglianza converte sempre utilizzando la conversione numerica (quindi `"0"` diventa `0`), mentre la conversione `Boolean` utilizza altre regole. +======= +From JavaScript's standpoint, this result is quite normal. An equality check converts values using the numeric conversion (hence `"0"` becomes `0`), while the explicit `Boolean` conversion uses another set of rules. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```` ## Uguaglianza stretta +<<<<<<< HEAD Un controllo di uguaglianza standard `==` ha dei problemi. Non distingue tra `0` e `false`: +======= +A regular equality check `==` has a problem. It cannot differentiate `0` from `false`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run alert( 0 == false ); // true ``` +<<<<<<< HEAD La stessa cosa accade con una stringa vuota: +======= +The same thing happens with an empty string: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run alert( '' == false ); // true ``` +<<<<<<< HEAD Questo perchè operandi di diversi tipi vengono convertiti a numeri dall'operatore di uguaglianza `==`. Una stringa vuota, come `false`, diventa zero. +======= +This happens because operands of different types are converted to numbers by the equality operator `==`. An empty string, just like `false`, becomes a zero. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Come possiamo fare se vogliamo distinguere tra `0` e `false`? @@ -132,18 +195,32 @@ Proviamolo: alert( 0 === false ); // false, because the types are different ``` +<<<<<<< HEAD Esiste anche un operatore di "disuguaglianza stretta" `!==`, come analogo per l'operatore `!=`. L'operatore di uguaglianza stretta è un pò più lungo da scrivere, ma rende ovvio il controllo e lascia meno spazio ad errori. +======= +There is also a "strict non-equality" operator `!==` analogous to `!=`. + +The strict equality operator is a bit longer to write, but makes it obvious what's going on and leaves less room for errors. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Confronto con null e undefined Diamo un'occhiata ad ulteriori casi limite. +<<<<<<< HEAD C'è un comportamento non atteso quando `null` o `undefined` vengono comparati con gli altri valori. Per un controllo di uguaglianza stretta `===` : Questi valori sono diversi, perchè ognuno di loro appartiene ad un tipo a se stante. +======= +There's a non-intuitive behavior when `null` or `undefined` are compared to other values. + + +For a strict equality check `===` +: These values are different, because each of them is a different type. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run alert( null === undefined ); // false @@ -156,10 +233,17 @@ Per un controllo non stretto `==` alert( null == undefined ); // true ``` +<<<<<<< HEAD Per confronti matematici `< > <= >=` : I valori `null/undefined` vengono convertiti in numeri: `null` diventa `0`, mentre `undefined` diventa `NaN`. Adesso notiamo una cosa divertente quando proviamo ad applicare queste regole. E, cosa più importante, come non cadere in tranelli a causa di queste caratteristiche. +======= +For maths and other comparisons `< > <= >=` +: `null/undefined` are converted to numbers: `null` becomes `0`, while `undefined` becomes `NaN`. + +Now let's see some funny things that happen when we apply these rules. And, what's more important, how to not fall into a trap with them. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ### Un risultato strano: null vs 0 @@ -171,15 +255,25 @@ alert( null == 0 ); // (2) false alert( null >= 0 ); // (3) *!*true*/!* ``` +<<<<<<< HEAD Si, matematicamente può sembrare strano. L'ultimo risultato dice che "`null` è maggiore o uguale a zero". Quindi uno dei confronti sopra dovrebbe essere corretto, ma risultano entrambi falsi. La ragione è che il controllo di uguaglianza `==` e di confronto `> < >= <=` lavorano diversamente. Il confronto converte `null` ad un numero, quindi lo tratta come `0`. Questo è perchè (3) `null >= 0` è vero e (1) `null > 0` è falso. +======= +Mathematically, that's strange. The last result states that "`null` is greater than or equal to zero", so in one of the comparisons above it must be `true`, but they are both false. + +The reason is that an equality check `==` and comparisons `> < >= <=` work differently. Comparisons convert `null` to a number, treating it as `0`. That's why (3) `null >= 0` is true and (1) `null > 0` is false. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb D'altra parte, il controllo di uguaglianza `==` per `undefined` e `null` viene definito, senza alcuna conversione, loro sono uguali l'un l'altro ma non sono uguali a nient'altro. Questo è il motivo per cui (2) `null == 0` è falsa. ### Undefined è incomparabile +<<<<<<< HEAD Il valore `undefined` non passa il confronto con nulla: +======= +The value `undefined` shouldn't be compared to other values: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run alert( undefined > 0 ); // false (1) @@ -187,25 +281,50 @@ alert( undefined < 0 ); // false (2) alert( undefined == 0 ); // false (3) ``` +<<<<<<< HEAD Perchè non va bene neanche uno zero? Sempre falso! Noi abbiamo questi risultati perchè: - Il confronto `(1)` e `(2)` ritorna `false` perchè `undefined` viene convertito a `NaN`. Mentre `NaN` è un valore numerico speciale che ritorna `false` con tutti i confronti. - Il confronto di uguaglianza `(3)` ritorna `false`, perchè `undefined` è uguale solo a `null` e a nessun altro valore. +======= +Why does it dislike zero so much? Always false! + +We get these results because: + +- Comparisons `(1)` and `(2)` return `false` because `undefined` gets converted to `NaN` and `NaN` is a special numeric value which returns `false` for all comparisons. +- The equality check `(3)` returns `false` because `undefined` only equals `null` and no other value. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ### Eludere i problemi +<<<<<<< HEAD Perchè abbiamo studiato questi esempi? Dovremmo ricordarci queste caratteristiche a memoria? Bhe, in realtà no. Questo tipo di trucchetti diventeranno familiari poco alla volta, ma c'è un modo sicure per eludere i problemi che possono sorgere. +======= +Why did we go over these examples? Should we remember these peculiarities all the time? Well, not really. Actually, these tricky things will gradually become familiar over time, but there's a solid way to evade problems with them: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Semplicemente tratte tutti i confronti con `undefined/null` solo con l'operatore di uguaglianza stretta `===`. +<<<<<<< HEAD Non utilizzate i confronti `>= > < <=` con una variabile che potrebbe valere `null/undefined`, senza essere davvero sicuri di cosa state facendo. Se una variabile può avere questi tipi di valore, è buona norma eseguire un controllo separatamente. +======= +Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you're really sure of what you're doing. If a variable can have these values, check for them separately. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Riepilogo +<<<<<<< HEAD - Gli operatori di confronto ritornano un valore booleano. - Le stringhe vengono confrontate lettera per lettera seguendo l'ordine "lessico-grafico". - Quando valori di tipi differenti vengono confrontati, questi vengono converiti a numeri (con eccezione per il controllo di uguaglianza stretto). - I valori `null` e `undefined` sono `==` solo l'uno per l'altro e a nessun altro valore. - Va prestata attenzione quando si utilizzano gli operatori di confronto come `>` o `<` con variabili che potrebbero contenere `null/undefined`. Una buona idea è eseguire un controllo separato per `null/undefined`. +======= +- Comparison operators return a boolean value. +- Strings are compared letter-by-letter in the "dictionary" order. +- When values of different types are compared, they get converted to numbers (with the exclusion of a strict equality check). +- The values `null` and `undefined` equal `==` each other and do not equal any other value. +- Be careful when using comparisons like `>` or `<` with variables that can occasionally be `null/undefined`. Checking for `null/undefined` separately is a good idea. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/09-alert-prompt-confirm/article.md b/1-js/02-first-steps/09-alert-prompt-confirm/article.md index ec2615e98..08e7b3703 100644 --- a/1-js/02-first-steps/09-alert-prompt-confirm/article.md +++ b/1-js/02-first-steps/09-alert-prompt-confirm/article.md @@ -2,7 +2,11 @@ Questa parte del tutorial ha l'intenzione di coprire JavaScript cosi per "com'è", senza i ritocchi specifici di ogni ambiente. +<<<<<<< HEAD Ma continueremo comunque ad utilizzare un browser come ambiente di test. Quindi dovremmo conoscere almeno un paio di funzioni dell'interfaccia utente. In questo capitolo prenderemo familiarità con le funzioni browser `alert`, `prompt` e `confirm`. +======= +But we'll still be using the browser as our demo environment, so we should know at least a few of its user-interface functions. In this chapter, we'll get familiar with the browser functions `alert`, `prompt` and `confirm`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## alert @@ -12,7 +16,11 @@ Sintassi: alert(message); ``` +<<<<<<< HEAD Questo mostra un messaggio e mette in pausa l'esecuzione dello script finchè l'utente non preme il pulsante "OK". +======= +This shows a message and pauses script execution until the user presses "OK". +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -20,27 +28,48 @@ Ad esempio: alert("Hello"); ``` +<<<<<<< HEAD La finestra che appare con il messaggio si chiama *modal window*. La parola "modal" significa che l'utente non potrà interagire con il resto della pagina, premere altri bottoni etc, fino a che non avrà interagito con la finestra. In questo esempio -- quando premerà "OK". ## prompt La funzione `prompt` accetta due argomenti: +======= +The mini-window with the message is called a *modal window*. The word "modal" means that the visitor can't interact with the rest of the page, press other buttons, etc. until they have dealt with the window. In this case -- until they press "OK". + +## prompt + +The function `prompt` accepts two arguments: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js no-beautify -result = prompt(title[, default]); +result = prompt(title, [default]); ``` +<<<<<<< HEAD Questo mostrerà una modal window con un messaggio testuale, un campo di input per l'utente ed il bottone OK/CANCEL. `title` : Il testo da mostrare all'utente. +======= +It shows a modal window with a text message, an input field for the visitor, and the buttons OK/CANCEL. + +`title` +: The text to show the visitor. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb `default` : Un secondo parametro opzionale, che rappresenta il valore iniziale del campo input. +<<<<<<< HEAD L'utente potrà scrivere nel campo input del prompt e successivamente premere OK. O in alternativa possono cancellare l'input premendo su CANCEL o la combinazione di tasti `key:Esc`. La chiamata ad un `prompt` ritorna il testo del campo input o `null` se è stato premuto cancel. +======= +The visitor may type something in the prompt input field and press OK. Or they can cancel the input by pressing CANCEL or hitting the `key:Esc` key. + +The call to `prompt` returns the text from the input field or `null` if the input was canceled. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -50,16 +79,27 @@ let age = prompt('How old are you?', 100); alert(`You are ${age} years old!`); // You are 100 years old! ``` +<<<<<<< HEAD ````warn header="IE: inserisce sempre un valore `default`" Il secondo parametro è opzionale. Ma se non inseriamo niente, Internet Explorer inserirà il testo `"undefined"` nel prompt. Provate ad eseguire il seguente codice su Internet Explorer: +======= +````warn header="In IE: always supply a `default`" +The second parameter is optional, but if we don't supply it, Internet Explorer will insert the text `"undefined"` into the prompt. + +Run this code in Internet Explorer to see: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let test = prompt("Test"); ``` +<<<<<<< HEAD Quindi, per farlo funzionare ugualmente su IE, è consigliato fornire sempre il secondo argomento: +======= +So, for prompts to look good in IE, we recommend always providing the second argument: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let test = prompt("Test", ''); // <-- for IE @@ -74,7 +114,11 @@ La sintassi: result = confirm(question); ``` +<<<<<<< HEAD La funzione `confirm` mostra una modal window con un `domanda` e due bottoni: OK e CANCEL. +======= +The function `confirm` shows a modal window with a `question` and two buttons: OK and CANCEL. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Il risultato è `true` se viene premuto OK altrimenti è `false`. @@ -88,22 +132,39 @@ alert( isBoss ); // true if OK is pressed ## Riepilogo +<<<<<<< HEAD Abbiamo osservato 3 funzioni specifiche dei browser per interagire con l'utente: +======= +We covered 3 browser-specific functions to interact with visitors: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb `alert` : mostra un messaggio. `prompt` +<<<<<<< HEAD : mostra un messaggio chiedendo all'utente un input testuale. Ritorna il testo o, se viene premuto CANCEL o il tasto `key:Esc`, tutti i browser ritornano `null`. +======= +: shows a message asking the user to input text. It returns the text or, if CANCEL or `key:Esc` is clicked, `null`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb `confirm` : mostra un messaggio e aspetta che l'utente prema "OK" o "CANCEL". Ritorna `true` se viene premuto OK e `false` per CANCEL/`key:Esc`. +<<<<<<< HEAD Tutti questi metodi sono dei modal window: quindi interrompono l'esecuzione dello script e non consentono all'utente di interagire con il resto della pagina finchè il messaggio non viene rimosso: +======= +All these methods are modal: they pause script execution and don't allow the visitor to interact with the rest of the page until the window has been dismissed. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ci sono due limitazioni che sono condivise da tutti i metodi visti sopra: +<<<<<<< HEAD 1. La posizione esatta della modal window viene decisa dal browser. Solitamente sta al centro. 2. Anche la grafica della modal window dipende dal browser. Non possiamo modificarla. +======= +1. The exact location of the modal window is determined by the browser. Usually, it's in the center. +2. The exact look of the window also depends on the browser. We can't modify it. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Questo è il prezzo da pagare per la semplicità. Ci sono altri modi di mostrare finestre carine, ricche di informazioni e interazioni con l'utente, ma se non ci interessa fare grandi cose, questi metodi possono essere utili. diff --git a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.png b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.png index 8c57b1885..04fb1fb0b 100644 Binary files a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.png and b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.png differ diff --git a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2@2x.png b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2@2x.png index cbb2c611a..08226b790 100644 Binary files a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2@2x.png and b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2@2x.png differ diff --git a/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task.png b/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task.png deleted file mode 100644 index 8b54dc83d..000000000 Binary files a/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task.png and /dev/null differ diff --git a/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task@2x.png b/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task@2x.png deleted file mode 100644 index 92001dfe8..000000000 Binary files a/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task@2x.png and /dev/null differ diff --git a/1-js/02-first-steps/10-ifelse/article.md b/1-js/02-first-steps/10-ifelse/article.md index f471c832c..dcbd86476 100644 --- a/1-js/02-first-steps/10-ifelse/article.md +++ b/1-js/02-first-steps/10-ifelse/article.md @@ -1,12 +1,22 @@ # Operatori condizionali: if, '?' +<<<<<<< HEAD Qualche volta abbiamo bisogno di eseguire certe azioni solo nel caso valgano determinate condizioni. Per questo c'è l'istruzione `if` e anche l'operatore condizionale di valutazione (ternario) a cui noi ci riferiremo con l'operatore "punto di domanda" `?` per semplicità. +======= +Sometimes, we need to perform different actions based on different conditions. + +To do that, we use the `if` statement and the conditional (ternary) operator which we will be referring to as the “question mark” operator `?` for simplicity. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## L'istruzione "if" +<<<<<<< HEAD L'istruzione `if` richiede una condizione, la valuta, e se il risultato è `true`, esegue il codice. +======= +The `if` statement evaluates a condition and, if the condition's result is `true`, executes a block of code. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -18,9 +28,15 @@ if (year == 2015) alert( 'You are right!' ); */!* ``` +<<<<<<< HEAD Nell'esempio sopra, la condizione è un semplice controllo di uguaglianza: `year == 2015`, ma potrebbe essere qualcosa di molto più complesso. Se dobbiamo eseguire più di un'istruzione, queste vanno raggruppate tramite le parentesi graffe: +======= +In the example above, the condition is a simple equality check (`year == 2015`), but it can be much more complex. + +If we want to execute more than one statement, we have to wrap our code block inside curly braces: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js if (year == 2015) { @@ -29,16 +45,29 @@ if (year == 2015) { } ``` +<<<<<<< HEAD E' consigliabile raggruppare sempre il codice all'interno delle parentesi graffe `{}` quando si usa un `if`, anche se contiene una sola istruzione. La leggibilità viene migliorata. +======= +We recommend wrapping your code block with curly braces `{}` every time you use an `if` statement, even if there is only one statement to execute. Doing so improves readability. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Conversione booleana +<<<<<<< HEAD L'istruzione `if (…)` valuta la condizione tra le parentesi e la converte al tipo booleano. +======= +The `if (…)` statement evaluates the expression in its parentheses and converts the result to a boolean. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ricordiamo le regole di conversione viste nel capitolo : +<<<<<<< HEAD - Il numero `0`, una stringa vuota `""`, `null`, `undefined` e `NaN` diventano `false`. Per questo vengono chiamati valori "falsi". - Gli altri valori diventano `true`, quindi vengono chiamati "veri". +======= +- A number `0`, an empty string `""`, `null`, `undefined`, and `NaN` all become `false`. Because of that they are called "falsy" values. +- Other values become `true`, so they are called "truthy". +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Quindi, il codice nell'esempio qui sotto, non verrà mai eseguito: @@ -48,7 +77,11 @@ if (0) { // 0 is falsy } ``` +<<<<<<< HEAD ...Invece nel prossimo esempio -- verrà eseguito sempre: +======= +...and inside this condition -- it always will: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js if (1) { // 1 is truthy @@ -56,7 +89,11 @@ if (1) { // 1 is truthy } ``` +<<<<<<< HEAD Possiamo anche passare un valore booleano già valutato, come qui: +======= +We can also pass a pre-evaluated boolean value to `if`, like this: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js let cond = (year == 2015); // equality evaluates to true or false @@ -68,11 +105,15 @@ if (cond) { ## La clausola "else" +<<<<<<< HEAD L'istruzione `if` può essere seguita da un blocco opzionale "else". Questo viene eseguito quando la condizione è falsa. +======= +The `if` statement may contain an optional "else" block. It executes when the condition is false. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: ```js run -let year = prompt('In which year was ECMAScript-2015 specification published?', ''); +let year = prompt('In which year was the ECMAScript-2015 specification published?', ''); if (year == 2015) { alert( 'You guessed it right!' ); @@ -83,12 +124,16 @@ if (year == 2015) { ## Condizione multiple: "else if" +<<<<<<< HEAD Qualche volta vorremmo testare diverse varianti di una condizione. Per questo esiste la clausola `else if`. +======= +Sometimes, we'd like to test several variants of a condition. The `else if` clause lets us do that. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: ```js run -let year = prompt('In which year was ECMAScript-2015 specification published?', ''); +let year = prompt('In which year was the ECMAScript-2015 specification published?', ''); if (year < 2015) { alert( 'Too early...' ); @@ -99,13 +144,23 @@ if (year < 2015) { } ``` +<<<<<<< HEAD Nel codice sopra JavaScript controlla prima `year < 2015`. Se risulta falso allora va alla successiva condizione `year > 2015`, altrimenti mostra il blocco else con l'`alert`. Ci possono essere molti blocchi `else if`. L'`else` finale è opzionale. +======= +In the code above, JavaScript first checks `year < 2015`. If that is falsy, it goes to the next condition `year > 2015`. If that is also falsy, it shows the last `alert`. + +There can be more `else if` blocks. The final `else` is optional. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Operatore Ternario '?' +<<<<<<< HEAD Qualche volta abbiamo bisogno di assegnare un valore ad una variabile in base ad una condizione. +======= +Sometimes, we need to assign a variable depending on a condition. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -124,16 +179,24 @@ if (age > 18) { alert(accessAllowed); ``` +<<<<<<< HEAD Esiste un'operatore "ternario" o "punto interrogativo" che ci consente di farlo in maniera più breve e semplice. +======= +The so-called "ternary" or "question mark" operator lets us do that in a shorter and simpler way. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb L'operatore viene rappresentato dal punto interrogativo `?`. Il termine formale è "ternario", il che significa che richiede tre operatori. E' l'unico operatore in JavaScript di questo tipo. La sintassi è: ```js -let result = condition ? value1 : value2 +let result = condition ? value1 : value2; ``` +<<<<<<< HEAD La `condition` viene valutata, se risulta viene ritornato il `value1`, altrimenti viene ritornato il -- `value2`. +======= +The `condition` is evaluated: if it's truthy then `value1` is returned, otherwise -- `value2`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -141,7 +204,13 @@ Ad esempio: let accessAllowed = (age > 18) ? true : false; ``` +<<<<<<< HEAD Tecnicamente, potremmo omettere le parentesi su `age > 18`. L'operatore ternario ha una precedenza molto bassa. Viene eseguito dopo gli operatori di confronto `>`, quindi il risultato sarebbe lo stesso: +======= +Technically, we can omit the parentheses around `age > 18`. The question mark operator has a low precedence, so it executes after the comparison `>`. + +This example will do the same thing as the previous one: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js // the comparison operator "age > 18" executes first anyway @@ -149,10 +218,17 @@ Tecnicamente, potremmo omettere le parentesi su `age > 18`. L'operatore ternario let accessAllowed = age > 18 ? true : false; ``` +<<<<<<< HEAD Le parentesi rendono però il codice più leggibile, quindi è consigliabile utilizzarle. ````smart Nell'esempio sopra sarebbe possibile evitare l'operatore ternario, perchè l'operatore di confronto ritorna già di suo `true/false`: +======= +But parentheses make the code more readable, so we recommend using them. + +````smart +In the example above, you can avoid using the question mark operator because the comparison itself returns `true/false`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js // the same @@ -162,7 +238,11 @@ let accessAllowed = age > 18; ## Operatori '?' multipli +<<<<<<< HEAD Una sequenza di operatori `?` consente di ritornare un valore che dipende da più condizioni. +======= +A sequence of question mark operators `?` can return a value that depends on more than one condition. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: ```js run @@ -176,6 +256,7 @@ let message = (age < 3) ? 'Hi, baby!' : alert( message ); ``` +<<<<<<< HEAD Potrebbe essere difficile inizialmente capirne la logica. Ma dopo averlo guardato da più vicino ci accorgiamo è una semplice sequenza di condizioni. 1. Il primo operatore "?" controlla `age < 3`. @@ -184,6 +265,16 @@ Potrebbe essere difficile inizialmente capirne la logica. Ma dopo averlo guardat 4. Se questo è vero -- ritorna `'Greetings!'`, altrimenti -- segue la colonna `":"` e ritorna `'What an unusual age!'`. La stessa logica viene usata con `if..else`: +======= +It may be difficult at first to grasp what's going on. But after a closer look, we can see that it's just an ordinary sequence of tests: + +1. The first question mark checks whether `age < 3`. +2. If true -- it returns `'Hi, baby!'`. Otherwise, it continues to the expression after the colon '":"', checking `age < 18`. +3. If that's true -- it returns `'Hello!'`. Otherwise, it continues to the expression after the next colon '":"', checking `age < 100`. +4. If that's true -- it returns `'Greetings!'`. Otherwise, it continues to the expression after the last colon '":"', returning `'What an unusual age!'`. + +Here's how this looks using `if..else`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js if (age < 3) { @@ -210,6 +301,7 @@ let company = prompt('Which company created JavaScript?', ''); */!* ``` +<<<<<<< HEAD In base al risultato della condizione `company == 'Netscape'`, viene eseguita la prima o la seconda parte, e mostra il giusto alert. Qui non assegnamo il risultato ad una variabile. L'idea è di eseguire codice differente in base alla condizione. @@ -219,6 +311,17 @@ Qui non assegnamo il risultato ad una variabile. L'idea è di eseguire codice di La notazione risulta essere molto più breve rispetto all'istruzione `if`, questo viene sfruttato da molti programmatori. Ma risulta meno leggibile. Lo stesso codice realizzato con un istruzione `if`: +======= +Depending on the condition `company == 'Netscape'`, either the first or the second expression after the `?` gets executed and shows an alert. + +We don't assign a result to a variable here. Instead, we execute different code depending on the condition. + +**We don't recommend using the question mark operator in this way.** + +The notation is shorter than the equivalent `if` statement, which appeals to some programmers. But it is less readable. + +Here is the same code using `if` for comparison: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run no-beautify let company = prompt('Which company created JavaScript?', ''); @@ -232,6 +335,12 @@ if (company == 'Netscape') { */!* ``` +<<<<<<< HEAD I nostri occhi scannerizzano il codice verticalmente. Quindi i costrutti che si estendono per qualche riga risultano più semplici da capire piuttosto di un'unica istruzione che si estende orrizontalmente. L'idea dell'operatore ternario `?` è di ritornare un valore piuttosto che un altro, in base al valore di una condizione. Va quindi utilizzato solo per questo tipo di situazioni. Invece per eseguire diversi codice è consigliabile utilizzare il costrutto `if`. +======= +Our eyes scan the code vertically. Code blocks which span several lines are easier to understand than a long, horizontal instruction set. + +The purpose of the question mark operator `?` is to return one value or another depending on its condition. Please use it for exactly that. Use `if` when you need to execute different branches of code. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.png b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.png new file mode 100644 index 000000000..32f0d4b94 Binary files /dev/null and b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.png differ diff --git a/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task@2x.png b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task@2x.png new file mode 100644 index 000000000..c3867e62c Binary files /dev/null and b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task@2x.png differ diff --git a/1-js/02-first-steps/10-ifelse/4-check-login/solution.md b/1-js/02-first-steps/11-logical-operators/9-check-login/solution.md similarity index 100% rename from 1-js/02-first-steps/10-ifelse/4-check-login/solution.md rename to 1-js/02-first-steps/11-logical-operators/9-check-login/solution.md diff --git a/1-js/02-first-steps/10-ifelse/4-check-login/task.md b/1-js/02-first-steps/11-logical-operators/9-check-login/task.md similarity index 72% rename from 1-js/02-first-steps/10-ifelse/4-check-login/task.md rename to 1-js/02-first-steps/11-logical-operators/9-check-login/task.md index e74a3c47b..aa377ce19 100644 --- a/1-js/02-first-steps/10-ifelse/4-check-login/task.md +++ b/1-js/02-first-steps/11-logical-operators/9-check-login/task.md @@ -20,6 +20,10 @@ Lo schema: Utilizzate blocchi `if` annidati. Tenete a mente anche la leggibilità del codice. +<<<<<<< HEAD:1-js/02-first-steps/10-ifelse/4-check-login/task.md Suggerimento: passare un input vuoto tramite prompr ritorna una stringa vuota `''`. Premere `key:ESC` con prompt aperto ritorna `null`. +======= +Hint: passing an empty input to a prompt returns an empty string `''`. Pressing `key:ESC` during a prompt returns `null`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb:1-js/02-first-steps/11-logical-operators/9-check-login/task.md [demo] diff --git a/1-js/02-first-steps/11-logical-operators/article.md b/1-js/02-first-steps/11-logical-operators/article.md index f1ed03e8a..2c4953148 100644 --- a/1-js/02-first-steps/11-logical-operators/article.md +++ b/1-js/02-first-steps/11-logical-operators/article.md @@ -2,7 +2,11 @@ In JavaScript ci sono tre operatori logici: `||` (OR), `&&` (AND), `!` (NOT). +<<<<<<< HEAD Nonostante si chiamino "logici", possono essere applicati a valori di qualsiasi tipo, non solo ai booleani. Il risultato stesso può essere di qualunque tipo. +======= +Although they are called "logical", they can be applied to values of any type, not only boolean. Their result can also be of any type. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Vediamoli nei dettagli. @@ -14,9 +18,15 @@ L'operatore "OR" viene rappresentato da due linee verticali: result = a || b; ``` +<<<<<<< HEAD Nella programmazione classica, l'OR logico è pensato per manipolare solo tipi booleani. Se almeno un argomento è `true`, allora il risultato sarà `true`, altrimenti sarà `false`. In JavaScript questo operatore è un pò più potente. Ma prima guardiamo come si comporta con valori booleani. +======= +In classical programming, the logical OR is meant to manipulate boolean values only. If any of its arguments are `true`, it returns `true`, otherwise it returns `false`. + +In JavaScript, the operator is a little bit trickier and more powerful. But first, let's see what happens with boolean values. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ci sono quattro combinazioni logiche possibili: @@ -29,9 +39,15 @@ alert( false || false ); // false Come possiamo vedere, il risultato è sempre `true` tranne nei casi in cui entrambi gli operandi sono `false`. +<<<<<<< HEAD Se un operando non è booleano, allora viene convertito in booleano per essere valutato. Ad esempio, il numero `1` viene visto come `true`, il numero `0` -- come `false`: +======= +If an operand is not a boolean, it's converted to a boolean for the evaluation. + +For instance, the number `1` is treated as `true`, the number `0` as `false`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run if (1 || 0) { // works just like if( true || false ) @@ -39,7 +55,11 @@ if (1 || 0) { // works just like if( true || false ) } ``` +<<<<<<< HEAD La maggior parte delle volte, OR `||` viene utilizzato in un `if` per verificare se *almeno una* delle condizioni è vera. +======= +Most of the time, OR `||` is used in an `if` statement to test if *any* of the given conditions is `true`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -64,9 +84,15 @@ if (hour < 10 || hour > 18 || isWeekend) { } ``` +<<<<<<< HEAD ## OR preleva il primo valore vero La logica descritta sopra è ovvia. Adesso proviamo ad addentrarci in qualche caratteristica "extra" di JavaScript. +======= +## OR finds the first truthy value + +The logic described above is somewhat classical. Now, let's bring in the "extra" features of JavaScript. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Si può estendere l'algoritmo come segue: @@ -78,9 +104,15 @@ result = value1 || value2 || value3; L'operatore OR `||` si comporta come segue: +<<<<<<< HEAD - Valuta gli operandi da sinistra a destra. - Ogni operando viene converito in booleano. Se il risultato è `true`, allora si ferma e ritorna il valore originale dell'operando. - Se tutti gli altri operandi sono stati valutati (ad esempio tutti erano `false`), ritorna l'ultimo operando. +======= +- Evaluates operands from left to right. +- For each operand, converts it to boolean. If the result is `true`, stops and returns the original value of that operand. +- If all operands have been evaluated (i.e. all were `false`), returns the last operand. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Un valore viene ritornato nella sua forma originale, non nella sua conversione booleana. @@ -97,6 +129,7 @@ alert( null || 0 || 1 ); // 1 (the first truthy value) alert( undefined || null || 0 ); // 0 (all falsy, returns the last value) ``` +<<<<<<< HEAD Questo ci porta ad alcuni utilizzi interessanti rispetto al "puro e classico OR booleano".boolean-only OR". 1. **Prelevare il primo valore vero da una lista di variabili o espressioni.** @@ -104,6 +137,15 @@ Questo ci porta ad alcuni utilizzi interessanti rispetto al "puro e classico OR Immaginiamo di avere diverse variabili, che possono contenere sia dati che `null/undefined`. Abbiamo bisogno di scegliere la prima che contiene dati. Possiamo utilizzare OR `||` per questo: +======= +This leads to some interesting usage compared to a "pure, classical, boolean-only OR". + +1. **Getting the first truthy value from a list of variables or expressions.** + + Imagine we have several variables which can either contain data or be `null/undefined`. How can we find the first one with data? + + We can use OR `||`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let currentUser = null; @@ -116,6 +158,7 @@ Questo ci porta ad alcuni utilizzi interessanti rispetto al "puro e classico OR alert( name ); // selects "John" – the first truthy value ``` +<<<<<<< HEAD Se entrambe `currentUser` e `defaultUser` sono false allora il risultato sarà `"unnamed"`. 2. **Valutazione a Corto-Circuito.** @@ -124,6 +167,16 @@ Questo ci porta ad alcuni utilizzi interessanti rispetto al "puro e classico OR Questo si vede chiaramente quando il secondo argomento causerebbe side-effect. Come l'assegnazione di una variabile. Se proviamo ad eseguire l'esempio che segue, `x` non verrà assegnata: +======= + If both `currentUser` and `defaultUser` were falsy, `"unnamed"` would be the result. +2. **Short-circuit evaluation.** + + Operands can be not only values, but arbitrary expressions. OR evaluates and tests them from left to right. The evaluation stops when a truthy value is reached, and the value is returned. This process is called "a short-circuit evaluation" because it goes as short as possible from left to right. + + This is clearly seen when the expression given as the second argument has a side effect like a variable assignment. + + In the example below, `x` does not get assigned: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run no-beautify let x; @@ -133,7 +186,11 @@ Questo ci porta ad alcuni utilizzi interessanti rispetto al "puro e classico OR alert(x); // undefined, because (x = 1) not evaluated ``` +<<<<<<< HEAD ...Se il primo argomento è `false`, allora `OR` prosegue e valuta il secondo, in questo caso l'assegnazione funziona: +======= + If, instead, the first argument is `false`, `||` evaluates the second one, thus running the assignment: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run no-beautify let x; @@ -143,11 +200,19 @@ Questo ci porta ad alcuni utilizzi interessanti rispetto al "puro e classico OR alert(x); // 1 ``` +<<<<<<< HEAD Un assegnazione è un caso semplice, potrebbero essere coinvolti altri tipi di side-effect. Quello che abbiamo visto, è un "modo breve di fare `if`". Il primo operando viene convertito a booleano e solo se è falso viene eseguito il secondo. La maggior parte delle volte è meglio utilizzare un " `if` "regolare", per mantenere il codice leggibile, in alcuni casi però può risultare utile. +======= + An assignment is a simple case. Other side effects can also be involved. + + As we can see, such a use case is a "shorter way of doing `if`". The first operand is converted to boolean. If it's false, the second one is evaluated. + + Most of time, it's better to use a "regular" `if` to keep the code easy to understand, but sometimes this can be handy. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## && (AND) @@ -157,7 +222,11 @@ L'operatore AND viene rappresentato con `&&`: result = a && b; ``` +<<<<<<< HEAD Nella programmazione classica AND ritorna `true` se entrambri gli operandi sono veri, altrimenti ritorna `false`: +======= +In classical programming, AND returns `true` if both operands are truthy and `false` otherwise: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run alert( true && true ); // true @@ -173,11 +242,15 @@ let hour = 12; let minute = 30; if (hour == 12 && minute == 30) { - alert( 'Time is 12:30' ); + alert( 'The time is 12:30' ); } ``` +<<<<<<< HEAD Proprio come per OR, qualsiasi valore è consentito come operando per AND: +======= +Just as with OR, any value is allowed as an operand of AND: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run if (1 && 0) { // evaluated as true && false @@ -186,7 +259,11 @@ if (1 && 0) { // evaluated as true && false ``` +<<<<<<< HEAD ## AND cerca il primo valore falso +======= +## AND finds the first falsy value +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Fornire più valori AND: @@ -196,9 +273,15 @@ result = value1 && value2 && value3; L'operatore AND `&&` si comporta come segue: +<<<<<<< HEAD - Valuta gli operandi da sinistra a destra. - Ogni operando viene convertito in booleano. Se il risultato è `false`, si ferma e ritorna il valore originale dell'operando. - Se tutti gli operandi precedenti sono stati valutati (ad esempio nel caso siano tutti veri) , ritorna l'ultimo operando. +======= +- Evaluates operands from left to right. +- For each operand, converts it to a boolean. If the result is `false`, stops and returns the original value of that operand. +- If all operands have been evaluated (i.e. all were truthy), returns the last operand. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb In altre parole, AND ritorna il primo valore falso se lo trova, altrimenti ritorna l'ultimo valore. @@ -233,7 +316,11 @@ alert( 1 && 2 && 3 ); // 3, the last one ````smart header="Precedenza di AND `&&` è maggiore dell'OR `||`" La precedenza dell'operatore AND`&&` è maggiore di quella dell'OR `||`. +<<<<<<< HEAD Quindi il codice `a && b || c && d` è praticamente uguale all'espressione: `(a && b) || (c && d)`. +======= +So the code `a && b || c && d` is essentially the same as if the `&&` expressions were in parentheses: `(a && b) || (c && d)`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```` Proprio come l'OR, anche AND `&&` può qualche volta rimpiazzare `if`. @@ -246,7 +333,11 @@ let x = 1; (x > 0) && alert( 'Greater than zero!' ); ``` +<<<<<<< HEAD Le azione nella parte destra di `&&` vengono eseguite solamente se la valutazione non si ferma prima. Cioè: solo se `(x > 0)` è vera. +======= +The action in the right part of `&&` would execute only if the evaluation reaches it. That is, only if `(x > 0)` is true. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Quindi sostanzialmente è analogo a: @@ -258,9 +349,15 @@ if (x > 0) { } ``` +<<<<<<< HEAD La variante con `&&` sembra essere più corta. Ma l'istruzione `if` è più ovvia e tende ad essere più leggibile. Quindi è consigliato usare ogni costrutto solo per i suoi scopi. Usate un `if` se volete imporre una condizione. Utilizzate invece `&&` se volete un AND. +======= +The variant with `&&` appears shorter. But `if` is more obvious and tends to be a little bit more readable. + +So we recommend using every construct for its purpose: use `if` if we want if and use `&&` if we want AND. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## ! (NOT) @@ -274,8 +371,13 @@ result = !value; L'operatore accetta un solo argomento e si comporta come segue: +<<<<<<< HEAD 1. Converte l'operando al tipo booleano: `true/false`. 2. Ritorna il valore inverso. +======= +1. Converts the operand to boolean type: `true/false`. +2. Returns the inverse value. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -291,7 +393,11 @@ alert( !!"non-empty string" ); // true alert( !!null ); // false ``` +<<<<<<< HEAD Quello che accade è che il primo NOT converte il tipo a booleano e ritorna il suo inverso, il secondo NOT lo inverte nuovamente. Alla fine abbiamo un valore di tipo booleano. +======= +That is, the first NOT converts the value to boolean and returns the inverse, and the second NOT inverses it again. In the end, we have a plain value-to-boolean conversion. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb C'è un modo molto più lungo per fare la stessa cosa -- una funzione del linguaggio `Boolean`: @@ -300,4 +406,8 @@ alert( Boolean("non-empty string") ); // true alert( Boolean(null) ); // false ``` +<<<<<<< HEAD La precedenza del NOT `!` è la più alta fra tutti gli operatori logici quindi viene sempre eseguita per prima, precede `&&`, `||`. +======= +The precedence of NOT `!` is the highest of all logical operators, so it always executes first, before `&&` or `||`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/12-while-for/7-list-primes/solution.md b/1-js/02-first-steps/12-while-for/7-list-primes/solution.md index 2fb8dcccc..39c30acc2 100644 --- a/1-js/02-first-steps/12-while-for/7-list-primes/solution.md +++ b/1-js/02-first-steps/12-while-for/7-list-primes/solution.md @@ -26,4 +26,8 @@ for (let i = 2; i <= n; i++) { // for each i... } ``` +<<<<<<< HEAD Ci sono molti modi per ottimizzarlo. Ad esempio, potremmo controllare i divisori di `2` fino alla radice di `i`. In ogni caso, se vogliamo essere veramete efficenti su grandi intervalli, abbiamo bisogno di cambiare approcio ed affidarci ad algoritmi matematici più avanzati e complessi, come [Quadratic sieve](https://en.wikipedia.org/wiki/Quadratic_sieve), [General number field sieve](https://en.wikipedia.org/wiki/General_number_field_sieve) etc. +======= +There's a lot of space to opimize it. For instance, we could look for the divisors from `2` to square root of `i`. But anyway, if we want to be really efficient for large intervals, we need to change the approach and rely on advanced maths and complex algorithms like [Quadratic sieve](https://en.wikipedia.org/wiki/Quadratic_sieve), [General number field sieve](https://en.wikipedia.org/wiki/General_number_field_sieve) etc. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/12-while-for/article.md b/1-js/02-first-steps/12-while-for/article.md index dda44463e..e7fc31581 100644 --- a/1-js/02-first-steps/12-while-for/article.md +++ b/1-js/02-first-steps/12-while-for/article.md @@ -1,10 +1,18 @@ # Cicli: while e for +<<<<<<< HEAD Abbiamo spesso bisogno di eseguire la stessa azione più volte di fila. Ad esempio, quando abbiamo bisogno di ritornare della merce da una lista una dopo l'altra. O anche solo eseguire lo stesso codice per ogni numero da 1 a 10. I *Cicli* sono un modo di ripetere la stessa parte di codice più volte. +======= +We often need to repeat actions. + +For example, outputting goods from a list one after another or just running the same code for each number from 1 to 10. + +*Loops* are a way to repeat the same code multiple times. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Il ciclo "while" @@ -31,11 +39,19 @@ while (i < 3) { // shows 0, then 1, then 2 Una singola esecuzione del corpo del ciclo viene chiamata *un iterazione*. Il ciclo nell'esempio sopra fa tre iterazioni. +<<<<<<< HEAD Se nell'esempio sopra non ci fosse `i++`, il ciclo si ripeterebbe per sempre (in teoria). Nella pratica, il browser ha dei metodi per bloccare questi cicli, con JavaScript server-side è necessario arrestare il processo. Qualsiasi espressione o variabile può essere utilizzata come condizione di un ciclo, non solo un confronto. Le espressioni vengono valutate e convertite al tipo bool dal ciclo `while`. Ad esempio, un modo più breve di scrivere `while (i != 0)` potrebbe essere `while (i)`: +======= +If `i++` was missing from the example above, the loop would repeat (in theory) forever. In practice, the browser provides ways to stop such loops, and in server-side JavaScript, we can kill the process. + +Any expression or variable can be a loop condition, not just comparisons: the condition is evaluated and converted to a boolean by `while`. + +For instance, a shorter way to write `while (i != 0)` is `while (i)`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let i = 3; @@ -68,7 +84,11 @@ do { } while (condition); ``` +<<<<<<< HEAD Il ciclo esegue prima il corpo, poi controlla la condizione, se questa è vera, esegue nuovamente il corpo. +======= +The loop will first execute the body, then check the condition, and, while it's truthy, execute it again and again. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -80,11 +100,19 @@ do { } while (i < 3); ``` +<<<<<<< HEAD Questa tipo di sintassi viene usata molto raramente ad eccezione dei casi in cui si vuole che il corpo del ciclo venga eseguito **almeno una volta** senza controllo sulla condizione. La forma più utilizzata è comunque: `while(…) {…}`. +======= +This form of syntax should only be used when you want the body of the loop to execute **at least once** regardless of the condition being truthy. Usually, the other form is preferred: `while(…) {…}`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Il ciclo "for" +<<<<<<< HEAD Il ciclo `for` è spesso il più utilizzato. +======= +The `for` loop is the most commonly used loop. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb La sua forma è del tipo: @@ -102,14 +130,28 @@ for (let i = 0; i < 3; i++) { // shows 0, then 1, then 2 } ``` +<<<<<<< HEAD Esaminiamo l'istruzione `for` parte per parte: +======= +Let's examine the `for` statement part-by-part: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb | Parte | | | |-------|----------|----------------------------------------------------------------------------| +<<<<<<< HEAD | begin | `i = 0` | Viene eseguito una volta all'entrata nel ciclo. | | condition | `i < 3`| Viene controllata prima di ogni iterazione del ciclo, se fallisce il ciclo si interrompe.| | step| `i++` | Viene eseguito prima del corpo ad ogni iterazione, ma dopo il controllo della condizione.| | body | `alert(i)`| Viene eseguito fino a che vale la condizione. +======= +| begin | `i = 0` | Executes once upon entering the loop. | +| condition | `i < 3`| Checked before every loop iteration. If false, the loop stops. | +| step| `i++` | Executes after the body on each iteration but before the condition check. | +| body | `alert(i)`| Runs again and again while the condition is truthy. | + + +The general loop algorithm works like this: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ``` Eseguiamo begin → (if condition → run body and run step) @@ -118,9 +160,15 @@ Eseguiamo begin → ... ``` +<<<<<<< HEAD Se i cicli vi sono nuovi, allora forse vi sarà d'aiuto tornare indietro agli esempi e provare a riprodurli passo-passo su un foglio di carta. Questo è quello che succede esattamente nel nostro codice: +======= +If you are new to loops, it could help to go back to the example and reproduce how it runs step-by-step on a piece of paper. + +Here's exactly what happens in our case: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js // for (let i = 0; i < 3; i++) alert(i) @@ -136,8 +184,13 @@ if (i < 3) { alert(i); i++ } // ...finish, because now i == 3 ``` +<<<<<<< HEAD ````smart header="Dichiarazioni di variabili inline" Qui il "counter" è una variabile `i` che viene dichiarata all'interno del ciclo. Questa viene chiamata una dichiarazione di una variabile "inline". Queste variabili sono visibile solo all'interno del ciclo. +======= +````smart header="Inline variable declaration" +Here, the "counter" variable `i` is declared right in the loop. This is called an "inline" variable declaration. Such variables are visible only inside the loop. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run for (*!*let*/!* i = 0; i < 3; i++) { @@ -146,7 +199,11 @@ for (*!*let*/!* i = 0; i < 3; i++) { alert(i); // error, no such variable ``` +<<<<<<< HEAD Invece che definire una nuova variabile, possiamo utilizzarne una già esistente: +======= +Instead of defining a variable, we could use an existing one: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let i = 0; @@ -187,9 +244,15 @@ for (; i < 3;) { } ``` +<<<<<<< HEAD Il ciclo diventa uguale ad un `while (i < 3)`. Possiamo rimuovere tutto, questo genererà un ciclo infinito: +======= +This makes the loop identical to `while (i < 3)`. + +We can actually remove everything, creating an infinite loop: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js for (;;) { @@ -197,15 +260,27 @@ for (;;) { } ``` +<<<<<<< HEAD Nota che le due `;` del ciclo `for` devono essere presenti, altrimenti sarebbe un errore di sintassi. +======= +Please note that the two `for` semicolons `;` must be present. Otherwise, there would be a syntax error. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Interromper un ciclo +<<<<<<< HEAD Normalmente un ciclo termina quando la condizione diventa falsa. Ma è possibile forzare l'uscita in qualsiasi momento. C'è una speciale direttiva `break` per fare questo. Ad esempio, il ciclo sotto chiede all'utente una serie di numeri, ma "termina" quando nessun numero viene inserito: +======= +Normally, a loop exits when its condition becomes falsy. + +But we can force the exit at any time using the special `break` directive. + +For example, the loop below asks the user for a series of numbers, "breaking" when no number is entered: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js let sum = 0; @@ -224,15 +299,27 @@ while (true) { alert( 'Sum: ' + sum ); ``` +<<<<<<< HEAD La direttiva `break` viene attivata alla linea `(*)` se l'utente inserisce una linea vuota o annulla la procedura di input. Questo fermerà il ciclo immediatamente, passando il controllo alla prima linea successiva al ciclo. In questo caso, `alert`. La combinazione "ciclo infinito + `break` quando necessario" è ottima per le situazioni in cui la condizione deve essere verificata in un punto differente dall'inizio/fine del ciclo, che può essere a metà, o in qualsiasi altro punto del corpo. +======= +The `break` directive is activated at the line `(*)` if the user enters an empty line or cancels the input. It stops the loop immediately, passing control to the first line after the loop. Namely, `alert`. + +The combination "infinite loop + `break` as needed" is great for situations when a loop's condition must be checked not in the beginning or end of the loop, but in the middle or even in several places of its body. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Vai alla prossima iterazione [#continue] +<<<<<<< HEAD La direttiva `continue` è una versione leggera del `break`. Non blocca l'intero ciclo. Invece interrompe solo l'iterazione corrente e forza il ciclo a reiniziare dall'iterazione successiva (se la condizione è soddisfatta). Possiamo utilizzarla se abbiamo finito con le operazioni che ci interessano in una data iterazione e vogliamo passare a quella seguente. +======= +The `continue` directive is a "lighter version" of `break`. It doesn't stop the whole loop. Instead, it stops the current iteration and forces the loop to start a new one (if the condition allows). + +We can use it if we're done with the current iteration and would like to move on to the next one. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Il ciclo sotto usa `continue` per ritornare i valori dispari: @@ -246,10 +333,17 @@ for (let i = 0; i < 10; i++) { } ``` +<<<<<<< HEAD Per i valori pari di `i`, la direttiva `continue` interrompe l'esecuzione del corpo e passa il controllo alla successiva iterazione del `for` (con il numero successivo). Quindi l'`alert` viene chiamato solo con i valori dispari. ````smart header="La direttiva `continue` aiuta a diminuire i livelli di nidificazione" Un ciclo che mostra i valori dispari potrebbe essere: +======= +For even values of `i`, the `continue` directive stops executing the body and passes control to the next iteration of `for` (with the next number). So the `alert` is only called for odd values. + +````smart header="The `continue` directive helps decrease nesting" +A loop that shows odd values could look like this: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js for (let i = 0; i < 10; i++) { @@ -261,6 +355,7 @@ for (let i = 0; i < 10; i++) { } ``` +<<<<<<< HEAD Dal punto di vista tecnico è identico all'esempio sopra. Ovviamente possiamo raccogliere il codice in un blocco `if` piuttosto di usare `continue`. Ma come effetto collaterale abbiamo aggiunto un livello di annidamento ulteriore (la chiamata `alert` all'interno delle parentesi graffe). Se il codice dentro `if` è più lungo di un paio di righe, si rischia di perdere in leggibilità. @@ -268,6 +363,15 @@ Ma come effetto collaterale abbiamo aggiunto un livello di annidamento ulteriore ````warn header="Vietato `break/continue` alla desta di '?'" Da notare che questo costrutto sintattico non è un espressione e non può quindi essere utilizzato con l'operatore ternario `?`. In particolare, direttive come `break/continue` non sono concesse. +======= +From a technical point of view, this is identical to the example above. Surely, we can just wrap the code in an `if` block instead of using `continue`. + +But as a side-effect, this created one more level of nesting (the `alert` call inside the curly braces). If the code inside of`if` is longer than a few lines, that may decrease the overall readability. +```` + +````warn header="No `break/continue` to the right side of '?'" +Please note that syntax constructs that are not expressions cannot be used with the ternary operator `?`. In particular, directives such as `break/continue` aren't allowed there. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio, se prendiamo questo codice: @@ -279,24 +383,39 @@ if (i > 5) { } ``` +<<<<<<< HEAD ...E lo riscriviamo utilizzando l'operatore ternario: +======= +...and rewrite it using a question mark: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js no-beautify -(i > 5) ? alert(i) : *!*continue*/!*; // continue not allowed here +(i > 5) ? alert(i) : *!*continue*/!*; // continue isn't allowed here ``` +<<<<<<< HEAD ...Questo smetterà di funzionare. Codice scritto cosi vi darà un errore di sintassi: Questa è solo un'altra ragione per cui non utilizzare l'operatore ternario `?` piuttosto che `if`. +======= +...it stops working. Code like this will give a syntax error: + + +This is just another reason not to use the question mark operator `?` instead of `if`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```` ## Etichette break/continue Qualche volta abbiamo bisogno di uscire da una serie di cicli annidati in un colpo solo. +<<<<<<< HEAD Ad esempio, nel codice sotto cicliamo su `i` e `j` eseguendo prompt sulle coordinate `(i, j)` da `(0,0)` a `(3,3)`: +======= +For example, in the code below we loop over `i` and `j`, prompting for the coordinates `(i, j)` from `(0,0)` to `(3,3)`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run no-beautify for (let i = 0; i < 3; i++) { @@ -315,7 +434,11 @@ alert('Done!'); Abbiamo bisogno di un modo per bloccare il processo se l'utente annulla l'input. +<<<<<<< HEAD Un semplice `break` dopo `input` interromperebbe solo il break interno. Questo non è sufficiente. Le etichette ci vengono in soccorso. +======= +The ordinary `break` after `input` would only break the inner loop. That's not sufficient--labels, come to the rescue! +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Una *label* (etichetta) è un identificatore seguito da ":" posti prima di un ciclo: ```js @@ -324,9 +447,13 @@ labelName: for (...) { } ``` +<<<<<<< HEAD L'istruzione `break ` nel ciclo uscirà fino alla label. Come nell'esempio: +======= +The `break ` statement in the loop below breaks out to the label: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run no-beautify *!*outer:*/!* for (let i = 0; i < 3; i++) { @@ -344,7 +471,11 @@ Come nell'esempio: alert('Done!'); ``` +<<<<<<< HEAD Nel codice sopra `break outer` va alla ricerca della label (etichetta) chiamata `outer` ed esce dal ciclo. +======= +In the code above, `break outer` looks upwards for the label named `outer` and breaks out of that loop. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Quindi il controllo va da `(*)` a `alert('Done!')`. @@ -355,10 +486,17 @@ outer: for (let i = 0; i < 3; i++) { ... } ``` +<<<<<<< HEAD Anche la direttiva `continue` può essere utilizzata con un'etichetta. In questo caso l'esecuzione salta alla prossima iterazione del ciclo con quell'etichetta. ````warn header="Label non equivalgono a \"goto\"" Le Label non permettono di saltare in un punto arbitrario del codice. +======= +The `continue` directive can also be used with a label. In this case, code execution jumps to the next iteration of the labeled loop. + +````warn header="Labels are not a \"goto\"" +Labels do not allow us to jump into an arbitrary place in the code. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio, non è possibile fare: ```js @@ -367,7 +505,11 @@ break label; // jumps to label? No. label: for (...) ``` +<<<<<<< HEAD La chiamata a `break/continue` è possibile solo dall'interno di un ciclo, e l'etichetta deve essere da qualche parte sopra la chiamata. +======= +A call to `break/continue` is only possible from inside a loop and the label must be somewhere above the directive. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```` ## Summary @@ -380,6 +522,12 @@ Abbiamo coperto 3 tipi di cicli: Per crere un ciclo infinito, si usa il costrutto `while(true)`. Questo tipo di cicli, come tutti gli altri, possono essere interrotti con la direttiva `break`. +<<<<<<< HEAD Se non si ha più intenzione di fare nulla nell'iterazione corrente e si vuole quindi saltare alla successiva, la direttiva `continue` lo consente. `break/continue` supportano le etichette prima del ciclo. Un etichetta è l'unico modo per `break/continue` di uscire da cicli annidati ed andare al ciclo esterno. +======= +If we don't want to do anything in the current iteration and would like to forward to the next one, we can use the `continue` directive. + +`break/continue` support labels before the loop. A label is the only way for `break/continue` to escape a nested loop to go to an outer one. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/02-first-steps/13-switch/article.md b/1-js/02-first-steps/13-switch/article.md index 601293a03..9de4db009 100644 --- a/1-js/02-first-steps/13-switch/article.md +++ b/1-js/02-first-steps/13-switch/article.md @@ -148,7 +148,7 @@ Mettiamo in risalto che il confronto di uguaglianza è sempre stretto. I valori Ad esempio, consideriamo il codice: ```js run -let arg = prompt("Enter a value?") +let arg = prompt("Enter a value?"); switch (arg) { case '0': case '1': @@ -163,7 +163,7 @@ switch (arg) { alert( 'Never executes!' ); break; default: - alert( 'An unknown value' ) + alert( 'An unknown value' ); } ``` diff --git a/1-js/02-first-steps/14-function-basics/article.md b/1-js/02-first-steps/14-function-basics/article.md index b45ecfa75..cc857c4bd 100644 --- a/1-js/02-first-steps/14-function-basics/article.md +++ b/1-js/02-first-steps/14-function-basics/article.md @@ -128,7 +128,11 @@ Le variabili dichiarate all'esterno di qualsiasi funzione, come `userName` nel c Le variabili globali sono visibili a qualsiasi funzione (se non sono oscurate da quelle locali). +<<<<<<< HEAD Solitamente, una funzione dichiara tutte le variabili necessarie per svolgere il compito. Le variabili locali vengono utilizzate per memorizzare dati relativi al progetto, quindi quando è importante che queste siano accessibili in qualsiasi punto del codice. I codici moderni cercano di evitare le variabili globali. La maggior parte delle variabili appartengono quindi a delle specifiche funzioni. +======= +Usually, a function declares all variables specific to its task. Global variables only store project-level data, and it's important that these variables are accessible from anywhere. Modern code has few or no globals. Most variables reside in their functions. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ``` ## Parametri @@ -204,8 +208,15 @@ function showMessage(from, text = anotherFunction()) { } ``` +<<<<<<< HEAD ```smart header="Valutazione dei parametri di default" In JavaScript, un parametro di default viene valutato ogni volta che viene chiamata una funzione senza i rispettivi parametri. Nell'esempio sopra, `anotherFunctions()` viene richiamata ogni volta che `someMessage()` viene richiamata senza il parametro `text`. Questo è in contrasto con altri linguaggi come Python, dove ogni parametro di default viene valutato solo durante la fase di interpretazione. +======= +```smart header="Evaluation of default parameters" + +In JavaScript, a default parameter is evaluated every time the function is called without the respective parameter. In the example above, `anotherFunction()` is called every time `showMessage()` is called without the `text` parameter. This is in contrast to some other languages like Python, where any default parameters are evaluated only once during the initial interpretation. + +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ``` @@ -370,9 +381,15 @@ Due azioni separate solitamente meritano due funzioni diverse, anche se molto sp Un paio di esempi che non rispettano queste regole: +<<<<<<< HEAD - `getAge` -- sarebbe un pessimo nome se mostrasse un `alert` con l'età (dovrebbe solo restituirlo). - `createForm` -- sarebbe un pessimo nome se modificasse il documento, aggiungendo il form (infatti dovrebbe solo crearlo e restituirlo). - `checkPermission` -- sarebbe un pessimo nome se mostrasse il messaggio `access granted/denied` (dovrebbe solo eseguire il controllo e ritornare il risultato). +======= +- `getAge` -- would be bad if it shows an `alert` with the age (should only get). +- `createForm` -- would be bad if it modifies the document, adding a form to it (should only create it and return). +- `checkPermission` -- would be bad if it displays the `access granted/denied` message (should only perform the check and return the result). +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Questi esempi assumono i significati comuni dei prefissi. Il loro significato dipende da voi e dal vostro team. E' comunque normale che il tuo codice abbia caratteristiche diverse. Ma è fondamentale avere una firma il cui prefisso sia sensato, che faccia capire cosa un determinato tipo di funzione può o non può fare. Tutte le funzione che iniziano con lo stesso prefisso dovrebbero seguire determinate regole. E' fondamentale che il team condivida queste informazioni. ``` diff --git a/1-js/02-first-steps/14-function-basics/function_basics.png b/1-js/02-first-steps/14-function-basics/function_basics.png index 5f3505811..f5e6f9418 100644 Binary files a/1-js/02-first-steps/14-function-basics/function_basics.png and b/1-js/02-first-steps/14-function-basics/function_basics.png differ diff --git a/1-js/02-first-steps/14-function-basics/function_basics@2x.png b/1-js/02-first-steps/14-function-basics/function_basics@2x.png index e2fe5bac0..c31b2636a 100644 Binary files a/1-js/02-first-steps/14-function-basics/function_basics@2x.png and b/1-js/02-first-steps/14-function-basics/function_basics@2x.png differ diff --git a/1-js/02-first-steps/16-javascript-specials/article.md b/1-js/02-first-steps/16-javascript-specials/article.md index 573c434ee..506a6089f 100644 --- a/1-js/02-first-steps/16-javascript-specials/article.md +++ b/1-js/02-first-steps/16-javascript-specials/article.md @@ -102,8 +102,13 @@ Di più in: e . Abbiamo utilizzato solo il browser come ambiente di sviluppo, quindi le interfacce di base saranno: +<<<<<<< HEAD [`prompt(question[, default])`](mdn:api/Window/prompt) : Pone una domanda `question`, e ritorna quello che l'utente ha inserito oppure `null` se ha premuto "cancel". +======= +[`prompt(question, [default])`](mdn:api/Window/prompt) +: Ask a `question`, and return either what the visitor entered or `null` if they pressed "cancel". +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb [`confirm(question)`](mdn:api/Window/confirm) : Pone una domanda `question` e fornisce la possibilità di scegliere tra Ok e Cancel. La scelta viene ritornata come `true/false`. @@ -148,8 +153,13 @@ Bit a Bit Ternari : C'è un solo operatore con tre parametri: `cond ? resultA : resultB`. Se `cond` è vera, ritorna `resultA`, altrimenti `resultB`. +<<<<<<< HEAD Operatori logici : AND logico `&&` e OR `||` eseguono delle valutazioni locali e ritornano un valore quando si fermano. +======= +Logical operators +: Logical AND `&&` and OR `||` perform short-circuit evaluation and then return the value where it stopped. Logical NOT `!` converts the operand to boolean type and returns the inverse value. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Confronto : Confronto di uguaglianza `==` valori di tipi diversi vengono convertiti in numeri (ad eccezione di `null` e `undefined` che si eguagliano tra di loro e con nient'altro), quindi questi sono uguali: @@ -167,8 +177,13 @@ Confronto Maggiore/minore confrontano le stringhe carattere per carattere, gli altri valori vengono convertiti a numeri. +<<<<<<< HEAD Operatori logici : Ce ne sono altri, come l'operatore virgola. +======= +Other operators +: There are few others, like a comma operator. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Di più in: , , . diff --git a/1-js/03-code-quality/01-debugging-chrome/article.md b/1-js/03-code-quality/01-debugging-chrome/article.md index 514f36060..f4924999e 100644 --- a/1-js/03-code-quality/01-debugging-chrome/article.md +++ b/1-js/03-code-quality/01-debugging-chrome/article.md @@ -56,11 +56,19 @@ Un *breakpoint* è un punto del codice in cui il debugger si metterà in pausa a Mentre il codice è in pause, è possibile esaminare le variabili, eseguire comandi tramite la console etc. In altre parole, possiamo eseguire il debug. +<<<<<<< HEAD Possiamo anche visualizzare la lista dei breakpoint nel pannello di destra. Questo pannello può risultare utile quando abbiamo più breakpoint in file diversi. Infatti ci consente di: - Salatare rapidamente ad un breakpoint (cliccando sopra al nome del breakpoint che ci interessa). - Disabilitare temporaneamente un breakpoint semplicemente togliendo la spunta. - Rimuovere breakpoint cliccando con il tasto destro e selezionando Rimuovi. - ...E molto altro. +======= +We can always find a list of breakpoints in the right pane. That's useful when we have many breakpoints in various files. It allows us to: +- Quickly jump to the breakpoint in the code (by clicking on it in the right pane). +- Temporarily disable the breakpoint by unchecking it. +- Remove the breakpoint by right-clicking and selecting Remove. +- ...And so on. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```smart header="Breakpoint condizionali" *Tasto destro* sul numero della riga ci consente di creare un breakpoint *condizionale*. Che viene attivato solo quando l'espressione fornita risulta vera. @@ -169,10 +177,17 @@ Se abbiamo abbastanza log nel nostro codice, possiamo vedere cosa sta accadendo ## Riepilogo +<<<<<<< HEAD Come abbiamo visto, ci sono tre diversi modi di metter in pausa uno script: 1. Un breakpoint. 2. L'istruzione `debugger`. 3. Un errore (solo se gli strumenti sviluppatore sono aperti ed è attivo il bottone ) +======= +As we can see, there are three main ways to pause a script: +1. A breakpoint. +2. The `debugger` statements. +3. An error (if dev tools are open and the button is "on"). +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Cosi possiamo esaminare le variabili e capire cosa è andato male durante l'esecuzione. diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.png b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.png index efa3c19df..abc59905a 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources@2x.png index e184bdd01..546615e30 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources@2x.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources@2x.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.png index 2fe449c9b..caf60ebb6 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint@2x.png index e4abc89d1..3f628b6ff 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint@2x.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint@2x.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.png index 98b22e777..0fc22b2ea 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console@2x.png index 3269a80f0..f01a7d101 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console@2x.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console@2x.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.png index 719293d2e..424ca26b0 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause@2x.png index 5c22ab361..04cc849d1 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause@2x.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause@2x.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.png index 1848ccfac..00507833a 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1@2x.png index fcabf722e..d2a38bf0c 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1@2x.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1@2x.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.png b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.png index ff91c531f..df9e13f3f 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs@2x.png index 09b10bf48..5793bd059 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs@2x.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs@2x.png differ diff --git a/1-js/03-code-quality/02-coding-style/1-style-errors/solution.md b/1-js/03-code-quality/02-coding-style/1-style-errors/solution.md index 7b6c32445..a5f9f9f4b 100644 --- a/1-js/03-code-quality/02-coding-style/1-style-errors/solution.md +++ b/1-js/03-code-quality/02-coding-style/1-style-errors/solution.md @@ -4,22 +4,22 @@ Avrete notato: ```js no-beautify function pow(x,n) // <- no space between arguments { // <- figure bracket on a separate line - let result=1; // <- no spaces to the both sides of = + let result=1; // <- no spaces before or after = for(let i=0;i>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Certamente, un team può utilizzare il proprio stile guida. Molte volte non serve. Ci sono molte opzioni tra cui scegliere, quindi scegliere tra una di queste generalmente è la scelta migliore. @@ -287,10 +291,17 @@ Molti linter sono integrati negli editor più popolari: è sufficiente attivare Ad esempio, per ESLint dovreste seguire quanto segue: +<<<<<<< HEAD 1. Installare [Node.JS](https://nodejs.org/). 2. Installare ESLint con il comando `npm install -g eslint` (npm è package installer di JavaScript). 3. Create un file di configurazione e rinominatelo `.eslintrc` nella root del vostro progetto JavaScript (la cartella che contiene tutti i file). 4. Installa/abilita il plugin per il tuo editor per integrare ESLint. La maggior parte degli editor ne ha uno. +======= +1. Install [Node.js](https://nodejs.org/). +2. Install ESLint with the command `npm install -g eslint` (npm is a JavaScript package installer). +3. Create a config file named `.eslintrc` in the root of your JavaScript project (in the folder that contains all your files). +4. Install/enable the plugin for your editor that integrates with ESLint. The majority of editors have one. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Qui un esempio di di un file `.eslintrc`: diff --git a/1-js/03-code-quality/02-coding-style/code-style.png b/1-js/03-code-quality/02-coding-style/code-style.png index a9ae4b98e..1c6d355dd 100644 Binary files a/1-js/03-code-quality/02-coding-style/code-style.png and b/1-js/03-code-quality/02-coding-style/code-style.png differ diff --git a/1-js/03-code-quality/02-coding-style/code-style@2x.png b/1-js/03-code-quality/02-coding-style/code-style@2x.png index be9b99dc2..832623d9c 100644 Binary files a/1-js/03-code-quality/02-coding-style/code-style@2x.png and b/1-js/03-code-quality/02-coding-style/code-style@2x.png differ diff --git a/1-js/03-code-quality/02-coding-style/figure-bracket-style.png b/1-js/03-code-quality/02-coding-style/figure-bracket-style.png index 112c2803e..b04db65c6 100644 Binary files a/1-js/03-code-quality/02-coding-style/figure-bracket-style.png and b/1-js/03-code-quality/02-coding-style/figure-bracket-style.png differ diff --git a/1-js/03-code-quality/02-coding-style/figure-bracket-style@2x.png b/1-js/03-code-quality/02-coding-style/figure-bracket-style@2x.png index ce6e75c4d..0e994ca4b 100644 Binary files a/1-js/03-code-quality/02-coding-style/figure-bracket-style@2x.png and b/1-js/03-code-quality/02-coding-style/figure-bracket-style@2x.png differ diff --git a/1-js/03-code-quality/04-ninja-code/article.md b/1-js/03-code-quality/04-ninja-code/article.md index 1e123c775..648b8d7bf 100644 --- a/1-js/03-code-quality/04-ninja-code/article.md +++ b/1-js/03-code-quality/04-ninja-code/article.md @@ -81,7 +81,11 @@ Quando scegliete un nome cercare di utilizzare parole più astratte possibili. C Dategli una possibilità. Un giovane iniziato potrebbe pensare -- sono veramente utili ad un ninja questi nomi? Infatti lo sono! +<<<<<<< HEAD Certamente il nome della variabile contiene comunque un significato. Infatti informa riguardo cosa è contenuto nella variabile: una stringa, un numero o qualcos'altro. Ma quando un estraneo cercherà di capire il codice, rimarrà sorpreso scoprendo che in realtà non forniscono alcuna informazione! Quindi fallirà nel suo intento di modificare il vostro codice. +======= + Sure, the variable name still means something. It says what's inside the variable: a string, a number or something else. But when an outsider tries to understand the code, they'll be surprised to see that there's actually no information at all! And will ultimately fail to alter your well-thought code. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Il tipo del valore è semplice da trovare con un debugger. Ma qual'è il significato della variabile? Quale numero/stringa contiene? @@ -151,7 +155,13 @@ function ninjaFunction(elem) { } ``` +<<<<<<< HEAD Un programmatore esterno che vorrebbe provare ad interagire con `elem` nella seconda parte della funzione, rimarrà sorpreso... Solamente in fase di debugging , dopo aver esaminato attentamente il codice si renderà conto che stava lavorando con un clone! +======= +A fellow programmer who wants to work with `elem` in the second half of the function will be surprised... Only during the debugging, after examining the code they will find out that they're working with a clone! + +Seen in code regularly. Deadly effective even against an experienced ninja. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Se ripetuto regolarmente nel codice, diventa letale anche contro i ninja più esperti. @@ -194,7 +204,11 @@ function render() { Un programmatore che si trova dentro `render` probabilmente non si accorgerà che la variabile locale `user` sta nascondendo quella esterna. +<<<<<<< HEAD Quindi potrebbe provare a lavorare con `user` pensando erroneamente che sia quella esterna, quella con il risultato di `authenticateUser()`... La trappola è servita! Addio debugger... +======= +Then they'll try to work with `user` assuming that it's the external variable, the result of `authenticateUser()`... The trap is sprung! Hello, debugger... +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Side-effect ovunque! diff --git a/1-js/03-code-quality/05-testing-mocha/article.md b/1-js/03-code-quality/05-testing-mocha/article.md index 8009cc25b..a528486b9 100644 --- a/1-js/03-code-quality/05-testing-mocha/article.md +++ b/1-js/03-code-quality/05-testing-mocha/article.md @@ -117,7 +117,7 @@ Per il futuro, vi faccio notare che ci sono dei test più avanzati, come [karma] Proviamo a fornire una semplice implementazione di `pow`, per passare il test: ```js -function pow() { +function pow(x, n) { return 8; // :) we cheat! } ``` @@ -233,7 +233,7 @@ Il raggruppamento viene effettuato con un `describe` annidato: describe("pow", function() { *!* - describe("raises x to power n", function() { + describe("raises x to power 3", function() { */!* function makeTest(x) { @@ -296,7 +296,11 @@ Testing finished – after all tests (after) [edit src="beforeafter" title="Open the example in the sandbox."] +<<<<<<< HEAD Solitamente, `beforeEach/afterEach` (`before/each`) vengono utilizzari per eseguire inizializzazioni, azzerare i contatori o fare qualcosa prima di iniziare il prossimo test. +======= +Usually, `beforeEach/afterEach` (`before/after`) are used to perform initialization, zero out counters or do something else between the tests (or test groups). +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```` ## Estendere le spec diff --git a/1-js/03-code-quality/05-testing-mocha/pow-2.view/index.html b/1-js/03-code-quality/05-testing-mocha/pow-2.view/index.html index d82a79dca..e8d6be23d 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-2.view/index.html +++ b/1-js/03-code-quality/05-testing-mocha/pow-2.view/index.html @@ -20,7 +20,7 @@ diff --git a/1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js index 10a032d03..e5ce2ce43 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js @@ -1,6 +1,6 @@ describe("pow", function() { - describe("raises x to power n", function() { + describe("raises x to power 3", function() { function makeTest(x) { let expected = x * x * x; diff --git a/1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js index a5a345979..75ff5e99f 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js @@ -1,6 +1,6 @@ describe("pow", function() { - describe("raises x to power n", function() { + describe("raises x to power 3", function() { function makeTest(x) { let expected = x * x * x; diff --git a/1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js index a5a345979..75ff5e99f 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js @@ -1,6 +1,6 @@ describe("pow", function() { - describe("raises x to power n", function() { + describe("raises x to power 3", function() { function makeTest(x) { let expected = x * x * x; diff --git a/1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js b/1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js index e7f63284f..db3283e49 100644 --- a/1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js +++ b/1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js @@ -1,7 +1,7 @@ function isEmpty(obj) { for (let key in obj) { - // if the loop has started, there is a prorty + // if the loop has started, there is a property return false; } return true; -} \ No newline at end of file +} diff --git a/1-js/04-object-basics/01-object/3-is-empty/solution.md b/1-js/04-object-basics/01-object/3-is-empty/solution.md index 4ec674d9a..a11c57cfb 100644 --- a/1-js/04-object-basics/01-object/3-is-empty/solution.md +++ b/1-js/04-object-basics/01-object/3-is-empty/solution.md @@ -1,3 +1,4 @@ +<<<<<<< HEAD E' sufficiente eseguire un ciclo e `return false` (ritornare falso) se l'oggetto contiene almeno una proprietà. ```js @@ -8,3 +9,6 @@ function isEmpty(obj) { return true; } ``` +======= +Just loop over the object and `return false` immediately if there's at least one property. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/04-object-basics/01-object/article.md b/1-js/04-object-basics/01-object/article.md index 0afa44996..f363afd86 100644 --- a/1-js/04-object-basics/01-object/article.md +++ b/1-js/04-object-basics/01-object/article.md @@ -218,7 +218,11 @@ alert(obj.__proto__); // [object Object], didn't work as intended Come possiamo veder dal codice, l'assegnazione alla primitiva `5` viene ignorata. +<<<<<<< HEAD Questa può diventare una situazione vulnerabile se abbiamo intenzione di memorizzare una coppia chiave-valore in un oggetto, consentendo al visitatore di specificare al chiave. +======= +That can become a source of bugs and even vulnerabilities if we intend to store arbitrary key-value pairs in an object, and allow a visitor to specify the keys. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb In questo caso il visitatore potrebbe scegliere "__proto__" come chiave, e l'assegnazione verrebbe rovinata (come abbiamo visto sopra). @@ -336,7 +340,7 @@ Per attraversare tutte le chiavi di un oggetto, esiste una speciale forma di cic La sintassi: ```js -for(key in object) { +for (key in object) { // executes the body for each key among object properties } ``` @@ -350,7 +354,7 @@ let user = { isAdmin: true }; -for(let key in user) { +for (let key in user) { // keys alert( key ); // name, age, isAdmin // values for the keys @@ -360,7 +364,11 @@ for(let key in user) { Da notare che tutti i costrutti "for" ci consentono di dichiarare delle variabili di ciclo da utilizzare all'interno del ciclo stesso, come `let key` in questo esempio. +<<<<<<< HEAD Inoltre possiamo utilizzare qualsiasi altr variabile al posto di `key`. Ad esempio `"for(let prop in obj)"` è molto utilizzato. +======= +Also, we could use another variable name here instead of `key`. For instance, `"for (let prop in obj)"` is also widely used. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ### Ordine degli oggetti @@ -381,7 +389,7 @@ let codes = { }; *!* -for(let code in codes) { +for (let code in codes) { alert(code); // 1, 41, 44, 49 } */!* @@ -439,7 +447,7 @@ let codes = { "+1": "USA" }; -for(let code in codes) { +for (let code in codes) { alert( +code ); // 49, 41, 44, 1 } ``` @@ -613,7 +621,7 @@ Possiamo anche utilizzare il metodo [Object.assign](mdn:js/Object/assign). La sintassi è: ```js -Object.assign(dest[, src1, src2, src3...]) +Object.assign(dest, [src1, src2, src3...]) ``` - Gli argomenti `dest`, e `src1, ..., srcN` (possono essere anche di più se necessario) sono oggetti. @@ -719,7 +727,14 @@ Operatori specifici: - Per controllare se un una proprietà con un certo nome esiste: `"key" in obj`. - Per iterare un oggetto: `for(let key in obj)`. +<<<<<<< HEAD Gli oggetti vengono assegnati e copiati per riferimento. In altre parole, la variabile non memorizza il "valore dell'oggetto", ma puittosto un "riferimento" (indirizzo di memoria). Quindi copiando questa variabile o passandola come argomento ad una funzione, fornirà un riferimento all'oggetto e non una copia. Tutte le operazioni effettuate su un oggetto copiato per riferimento (come aggiungere/rimuovere proprietà) vengono effettuate sullo stesso oggetto. +======= +Additional operators: +- To delete a property: `delete obj.prop`. +- To check if a property with the given key exists: `"key" in obj`. +- To iterate over an object: `for (let key in obj)` loop. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Per fare una "copia" (un clone) possiamo utilizzare `Object.assign` oppure [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep). diff --git a/1-js/04-object-basics/01-object/object-user-delete.png b/1-js/04-object-basics/01-object/object-user-delete.png index 688158f9b..8702675c8 100644 Binary files a/1-js/04-object-basics/01-object/object-user-delete.png and b/1-js/04-object-basics/01-object/object-user-delete.png differ diff --git a/1-js/04-object-basics/01-object/object-user-delete@2x.png b/1-js/04-object-basics/01-object/object-user-delete@2x.png index e1ef65541..698766bb0 100644 Binary files a/1-js/04-object-basics/01-object/object-user-delete@2x.png and b/1-js/04-object-basics/01-object/object-user-delete@2x.png differ diff --git a/1-js/04-object-basics/01-object/object-user-empty.png b/1-js/04-object-basics/01-object/object-user-empty.png index 80fdc0d3b..6b1f27a69 100644 Binary files a/1-js/04-object-basics/01-object/object-user-empty.png and b/1-js/04-object-basics/01-object/object-user-empty.png differ diff --git a/1-js/04-object-basics/01-object/object-user-empty@2x.png b/1-js/04-object-basics/01-object/object-user-empty@2x.png index 8db894cb3..5f261eca4 100644 Binary files a/1-js/04-object-basics/01-object/object-user-empty@2x.png and b/1-js/04-object-basics/01-object/object-user-empty@2x.png differ diff --git a/1-js/04-object-basics/01-object/object-user-isadmin.png b/1-js/04-object-basics/01-object/object-user-isadmin.png index 4e76eeb76..2ce66a49d 100644 Binary files a/1-js/04-object-basics/01-object/object-user-isadmin.png and b/1-js/04-object-basics/01-object/object-user-isadmin.png differ diff --git a/1-js/04-object-basics/01-object/object-user-isadmin@2x.png b/1-js/04-object-basics/01-object/object-user-isadmin@2x.png index b40977690..4a15dac64 100644 Binary files a/1-js/04-object-basics/01-object/object-user-isadmin@2x.png and b/1-js/04-object-basics/01-object/object-user-isadmin@2x.png differ diff --git a/1-js/04-object-basics/01-object/object-user-props.png b/1-js/04-object-basics/01-object/object-user-props.png index 2bfdfabdb..b0486e900 100644 Binary files a/1-js/04-object-basics/01-object/object-user-props.png and b/1-js/04-object-basics/01-object/object-user-props.png differ diff --git a/1-js/04-object-basics/01-object/object-user-props@2x.png b/1-js/04-object-basics/01-object/object-user-props@2x.png index 4935b59ce..20859fe91 100644 Binary files a/1-js/04-object-basics/01-object/object-user-props@2x.png and b/1-js/04-object-basics/01-object/object-user-props@2x.png differ diff --git a/1-js/04-object-basics/01-object/object-user.png b/1-js/04-object-basics/01-object/object-user.png index 16179209f..6215b8207 100644 Binary files a/1-js/04-object-basics/01-object/object-user.png and b/1-js/04-object-basics/01-object/object-user.png differ diff --git a/1-js/04-object-basics/01-object/object-user@2x.png b/1-js/04-object-basics/01-object/object-user@2x.png index 720389532..c66fa5159 100644 Binary files a/1-js/04-object-basics/01-object/object-user@2x.png and b/1-js/04-object-basics/01-object/object-user@2x.png differ diff --git a/1-js/04-object-basics/01-object/object.png b/1-js/04-object-basics/01-object/object.png index f94d094a9..a853c9c39 100644 Binary files a/1-js/04-object-basics/01-object/object.png and b/1-js/04-object-basics/01-object/object.png differ diff --git a/1-js/04-object-basics/01-object/object@2x.png b/1-js/04-object-basics/01-object/object@2x.png index 003c2f6ea..12011ff5c 100644 Binary files a/1-js/04-object-basics/01-object/object@2x.png and b/1-js/04-object-basics/01-object/object@2x.png differ diff --git a/1-js/04-object-basics/01-object/variable-contains-reference.png b/1-js/04-object-basics/01-object/variable-contains-reference.png index d6e7fddff..cdd53d0b2 100644 Binary files a/1-js/04-object-basics/01-object/variable-contains-reference.png and b/1-js/04-object-basics/01-object/variable-contains-reference.png differ diff --git a/1-js/04-object-basics/01-object/variable-contains-reference@2x.png b/1-js/04-object-basics/01-object/variable-contains-reference@2x.png index 145bad29a..070126198 100644 Binary files a/1-js/04-object-basics/01-object/variable-contains-reference@2x.png and b/1-js/04-object-basics/01-object/variable-contains-reference@2x.png differ diff --git a/1-js/04-object-basics/01-object/variable-copy-reference.png b/1-js/04-object-basics/01-object/variable-copy-reference.png index 97510c4b2..287085842 100644 Binary files a/1-js/04-object-basics/01-object/variable-copy-reference.png and b/1-js/04-object-basics/01-object/variable-copy-reference.png differ diff --git a/1-js/04-object-basics/01-object/variable-copy-reference@2x.png b/1-js/04-object-basics/01-object/variable-copy-reference@2x.png index a64238a52..e7b994c53 100644 Binary files a/1-js/04-object-basics/01-object/variable-copy-reference@2x.png and b/1-js/04-object-basics/01-object/variable-copy-reference@2x.png differ diff --git a/1-js/04-object-basics/01-object/variable-copy-value.png b/1-js/04-object-basics/01-object/variable-copy-value.png index e21af0990..c360a0e13 100644 Binary files a/1-js/04-object-basics/01-object/variable-copy-value.png and b/1-js/04-object-basics/01-object/variable-copy-value.png differ diff --git a/1-js/04-object-basics/01-object/variable-copy-value@2x.png b/1-js/04-object-basics/01-object/variable-copy-value@2x.png index 2f0b2f47d..323bc4622 100644 Binary files a/1-js/04-object-basics/01-object/variable-copy-value@2x.png and b/1-js/04-object-basics/01-object/variable-copy-value@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-delete-refs.png b/1-js/04-object-basics/02-garbage-collection/family-delete-refs.png index 5c10f0e47..05160890e 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-delete-refs.png and b/1-js/04-object-basics/02-garbage-collection/family-delete-refs.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-delete-refs@2x.png b/1-js/04-object-basics/02-garbage-collection/family-delete-refs@2x.png index 24d29630e..a92fc573b 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-delete-refs@2x.png and b/1-js/04-object-basics/02-garbage-collection/family-delete-refs@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-family.png b/1-js/04-object-basics/02-garbage-collection/family-no-family.png index a4ce30a35..e60f9b88d 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-no-family.png and b/1-js/04-object-basics/02-garbage-collection/family-no-family.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-family@2x.png b/1-js/04-object-basics/02-garbage-collection/family-no-family@2x.png index 0d9949839..754903706 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-no-family@2x.png and b/1-js/04-object-basics/02-garbage-collection/family-no-family@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-father-2.png b/1-js/04-object-basics/02-garbage-collection/family-no-father-2.png index e24dba5b5..823d0a618 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-no-father-2.png and b/1-js/04-object-basics/02-garbage-collection/family-no-father-2.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-father-2@2x.png b/1-js/04-object-basics/02-garbage-collection/family-no-father-2@2x.png index a6c4ee36a..9e100d522 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-no-father-2@2x.png and b/1-js/04-object-basics/02-garbage-collection/family-no-father-2@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-father.png b/1-js/04-object-basics/02-garbage-collection/family-no-father.png index df14624bc..5ddb6f68f 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-no-father.png and b/1-js/04-object-basics/02-garbage-collection/family-no-father.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-father@2x.png b/1-js/04-object-basics/02-garbage-collection/family-no-father@2x.png index 5ab4b3792..868c748a0 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-no-father@2x.png and b/1-js/04-object-basics/02-garbage-collection/family-no-father@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family.png b/1-js/04-object-basics/02-garbage-collection/family.png index dbbc01d2f..f37464e6c 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family.png and b/1-js/04-object-basics/02-garbage-collection/family.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family@2x.png b/1-js/04-object-basics/02-garbage-collection/family@2x.png index 64b4619ba..4f5d47210 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family@2x.png and b/1-js/04-object-basics/02-garbage-collection/family@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-1.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-1.png index 423191778..5cfe664c3 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-1.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-1.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-1@2x.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-1@2x.png index 223ea32a1..cf93a1605 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-1@2x.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-1@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-2.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-2.png index da63d3969..2bbe4241d 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-2.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-2.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-2@2x.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-2@2x.png index 1f614e3e6..f4a7abb52 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-2@2x.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-2@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-3.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-3.png index e77144c1d..665a22784 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-3.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-3.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-3@2x.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-3@2x.png index 37e349b62..60d4059cc 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-3@2x.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-3@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-4.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-4.png index 110e0d9c4..4ba6e17ec 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-4.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-4.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-4@2x.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-4@2x.png index c09d75f9d..8ac09c80d 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-4@2x.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-4@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-5.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-5.png index bc4ea9670..35c8816dc 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-5.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-5.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-5@2x.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-5@2x.png index 0ab697e68..4db0c1732 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-5@2x.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-5@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin.png b/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin.png index 29c4fcbea..9ddac3e29 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin.png and b/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin@2x.png b/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin@2x.png index 2f80f19a2..9069781f2 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin@2x.png and b/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost.png b/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost.png index cdc1d4904..ae1684b2d 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost.png and b/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost@2x.png b/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost@2x.png index d58afdb58..c510380f9 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost@2x.png and b/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/memory-user-john.png b/1-js/04-object-basics/02-garbage-collection/memory-user-john.png index 3ba5730de..2ad00b6cb 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/memory-user-john.png and b/1-js/04-object-basics/02-garbage-collection/memory-user-john.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/memory-user-john@2x.png b/1-js/04-object-basics/02-garbage-collection/memory-user-john@2x.png index 5aa81bb0c..f365ac036 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/memory-user-john@2x.png and b/1-js/04-object-basics/02-garbage-collection/memory-user-john@2x.png differ diff --git a/1-js/04-object-basics/03-symbol/article.md b/1-js/04-object-basics/03-symbol/article.md index 146f80b56..dae0defed 100644 --- a/1-js/04-object-basics/03-symbol/article.md +++ b/1-js/04-object-basics/03-symbol/article.md @@ -18,7 +18,7 @@ let id = Symbol(); Possiamo fornire anche una descrizione al symbol (chiamata nome del symbol), utile per il debugging: -```js +```js run // id is a symbol with the description "id" let id = Symbol("id"); ``` @@ -49,7 +49,14 @@ let id = Symbol("id"); alert(id); // TypeError: Cannot convert a Symbol value to a string */!* ``` +<<<<<<< HEAD Se vogliamo veramente mostrare un symbol, dobbiamo utilizzare `.toString()`: +======= + +That's a "language guard" against messing up, because strings and symbols are fundamentally different and should not occasionally convert one into another. + +If we really want to show a symbol, we need to call `.toString()` on it, like here: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let id = Symbol("id"); *!* @@ -57,7 +64,18 @@ alert(id.toString()); // Symbol(id), now it works */!* ``` +<<<<<<< HEAD Questo blocco è un "controllo di linguaggio" contro gli errori accidentali, perché le stringhe e i symbol sono fondamentalmente differenti e spesso non dovrebbe essere necessario convertirli. +======= +Or get `symbol.description` property to get the description only: +```js run +let id = Symbol("id"); +*!* +alert(id.description); // id +*/!* +``` + +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```` ## Proprietà "nascoste" diff --git a/1-js/04-object-basics/04-object-methods/7-calculator/solution.md b/1-js/04-object-basics/04-object-methods/7-calculator/solution.md index 22c4bf187..459997624 100644 --- a/1-js/04-object-basics/04-object-methods/7-calculator/solution.md +++ b/1-js/04-object-basics/04-object-methods/7-calculator/solution.md @@ -1,6 +1,5 @@ - -```js run demo +```js run demo solution let calculator = { sum() { return this.a + this.b; @@ -20,4 +19,3 @@ calculator.read(); alert( calculator.sum() ); alert( calculator.mul() ); ``` - diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md b/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md index b2bea5ad5..f67d562aa 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md @@ -1,6 +1,6 @@ La soluzione sta nel ritornare l'oggetto stesso ad ogni chiamata. -```js run +```js run demo let ladder = { step: 0, up() { @@ -28,7 +28,7 @@ ladder.up().up().down().up().down().showStep(); // 1 Possiamo anche scrivere una singola chiamata per riga. Per catene molto lunghe diventa più leggibile: -```js +```js ladder .up() .up() @@ -37,4 +37,3 @@ ladder .down() .showStep(); // 1 ``` - diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md index 7fad53b59..d712845c2 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md @@ -30,7 +30,11 @@ ladder.down(); ladder.showStep(); // 1 ``` +<<<<<<< HEAD Modificare il codice di `up` e `down` per rendere le chiamate concatenabili, come in questo esempio: +======= +Modify the code of `up`, `down` and `showStep` to make the calls chainable, like this: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js ladder.up().up().down().showStep(); // 1 diff --git a/1-js/04-object-basics/04-object-methods/article.md b/1-js/04-object-basics/04-object-methods/article.md index 609650fea..2d0f3ec41 100644 --- a/1-js/04-object-basics/04-object-methods/article.md +++ b/1-js/04-object-basics/04-object-methods/article.md @@ -63,7 +63,11 @@ user.sayHi(); // Hello! ```smart header="Programmazione orientata agli oggetti" Quando scriviamo codice utilizzando gli oggetti per rappresentare le entità, questa viene definita [programmazione orientata agli oggetti](https://en.wikipedia.org/wiki/Object-oriented_programming), in breve: "OOP". +<<<<<<< HEAD OOP è una grande cosa, un ambito di interesse con i propri studi. Come scegliere le giuste entità? Come organizzare le interazioni tra loro? Questa è l'architettura di un codice, e ci sono molti libri importanti che trattano questo argomento, come "Design Patterns: Elements of Reusable Object-Oriented Software" by E.Gamma, R.Helm, R.Johnson, J.Vissides or "Object-Oriented Analysis and Design with Applications" by G.Booch. Noi cercheremo di capire i concetti chiave nel capitolo . +======= +OOP is a big thing, an interesting science of its own. How to choose the right entities? How to organize the interaction between them? That's architecture, and there are great books on that topic, like "Design Patterns: Elements of Reusable Object-Oriented Software" by E.Gamma, R.Helm, R.Johnson, J.Vissides or "Object-Oriented Analysis and Design with Applications" by G.Booch, and more. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ``` ### La forma breve dei metodi @@ -214,7 +218,11 @@ sayHi(); // undefined In questo caso `this` è `undefined` in modalità strict. Se tentiamo di accedere a `this.name`, ci sarà un errore. +<<<<<<< HEAD Se non è attiva la modalità strict (quindi se ci dimentichiamo `use strict`) il valor di `this` in questo sarà *l'oggetto globale* (`window` in un browser, lo studieremo più avanti). Questo strano comportamento ha delle motivazioni storiche, che `"use strict"` sistema. +======= +In non-strict mode the value of `this` in such case will be the *global object* (`window` in a browser, we'll get to it later in the chapter [](info:global-object)). This is a historical behavior that `"use strict"` fixes. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Va detto che solitamente una chiamata a funzione che utilizza `this` senza un oggetto non è comune, nella maggior part dei casi è un errore di programmazione. Se una funzione utilizza `this`, ha senso che venga invocata nel contesto di un oggetto. @@ -257,7 +265,13 @@ Nell'ultima riga c'è un operatore ternario che deve decidere tra `user.hi` o `u Il metodo viene immediatamente chiamato con le parentesi `()`. Ma non funziona! +<<<<<<< HEAD Questo funziona (oggetto punto metodo): +======= +You can see that the call results in an error, because the value of `"this"` inside the call becomes `undefined`. + +This works (object dot method): +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js user.hi(); ``` diff --git a/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/solution.md b/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/solution.md index e5583c5d0..86bb65416 100644 --- a/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/solution.md +++ b/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/solution.md @@ -1,5 +1,3 @@ - - ```js run demo function Calculator() { diff --git a/1-js/04-object-basics/06-constructor-new/article.md b/1-js/04-object-basics/06-constructor-new/article.md index 86dd89cfb..951496a27 100644 --- a/1-js/04-object-basics/06-constructor-new/article.md +++ b/1-js/04-object-basics/06-constructor-new/article.md @@ -226,5 +226,9 @@ JavaScript fornisce costruttori per la maggior parte degli oggetti integrati nel ```smart header="Oggetti, ci ritorneremo!" In questo capitolo abbiamo coperto solamente le basi degli oggetti e dei costruttori. Era necessario conoscerne le basi per capire meglio riguardo i data types e le funzioni che studieremo nel prossimo capitolo. +<<<<<<< HEAD Dopo averli imparati, nel capitolo ritorneremo sugli oggetti e li copriremo più in profondità, coprendo anche i concetti di ereditarietà e classi. +======= +After we learn that, we return to objects and cover them in-depth in the chapters and . +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ``` diff --git a/1-js/05-data-types/01-primitives-methods/article.md b/1-js/05-data-types/01-primitives-methods/article.md index f1e2f8196..9208f895e 100644 --- a/1-js/05-data-types/01-primitives-methods/article.md +++ b/1-js/05-data-types/01-primitives-methods/article.md @@ -46,9 +46,15 @@ Questo è il paradosso contro cui si è scontato il creatore di JavaScript: La soluzione sembra un po' strana: +<<<<<<< HEAD 1. Le primitive rimangono primitive. Contengono un singolo valore. 2. Il linguaggio consente di accedere alle proprietà e ai metodi di stringhe, numeri, booleani e symbol. 3. Quando questo accade, viene creato uno speciale "oggetto contenitore" che fornisce le funzionalità extra, successivamente verrà distrutto. +======= +1. Primitives are still primitive. A single value, as desired. +2. The language allows access to methods and properties of strings, numbers, booleans and symbols. +3. When this happens, a special "object wrapper" that provides the extra functionality is created, and then is destroyed. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Gli "oggetti contenitore" sono diversi per ogni primitiva e sono chiamati: `String`, `Number`, `Boolean` e `Symbol`. Questi forniscono diversi insiemi di metodi. diff --git a/1-js/05-data-types/02-number/8-random-min-max/solution.md b/1-js/05-data-types/02-number/8-random-min-max/solution.md index 678cababa..2f48667b8 100644 --- a/1-js/05-data-types/02-number/8-random-min-max/solution.md +++ b/1-js/05-data-types/02-number/8-random-min-max/solution.md @@ -2,8 +2,13 @@ Abbiamo bisogno di far "scorrere" 'intervallo da 0..1 a `min`.. `max`. Questo può essere ottenuto con due passi: +<<<<<<< HEAD 1. Se moltiplichiamo un numero casuale compreso tra 0..1 per `max-min`, l'intervallo dei possibili valori crese da `0..1` a `0..max-min`. 2. Ora se aggiungiamo `min`, il possibile intervallo diventa `min` - `max`. +======= +1. If we multiply a random number from 0..1 by `max-min`, then the interval of possible values increases `0..1` to `0..max-min`. +2. Now if we add `min`, the possible interval becomes from `min` to `max`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb La funzione: diff --git a/1-js/05-data-types/02-number/9-random-int-min-max/task.md b/1-js/05-data-types/02-number/9-random-int-min-max/task.md index 4e595b4a5..9a8502d97 100644 --- a/1-js/05-data-types/02-number/9-random-int-min-max/task.md +++ b/1-js/05-data-types/02-number/9-random-int-min-max/task.md @@ -12,9 +12,9 @@ Qualsiasi numero nell'intervallo `min..max` deve poter apparire con la stessa pr Esempi: ```js -alert( random(1, 5) ); // 1 -alert( random(1, 5) ); // 3 -alert( random(1, 5) ); // 5 +alert( randomInteger(1, 5) ); // 1 +alert( randomInteger(1, 5) ); // 3 +alert( randomInteger(1, 5) ); // 5 ``` Potete utilizzare la soluzione dell'[esercizio precedente](info:task/random-min-max) come base. diff --git a/1-js/05-data-types/02-number/article.md b/1-js/05-data-types/02-number/article.md index a586ae06d..0af92d8c5 100644 --- a/1-js/05-data-types/02-number/article.md +++ b/1-js/05-data-types/02-number/article.md @@ -26,11 +26,15 @@ In altre parole, `"e"` moltiplica il numero `1` seguito dal numero di zeri dati. ```js 1e3 = 1 * 1000 -1.23e6 = 1.23 * 1000000 +1.23e6 = 1.23 * 1000000 ``` +<<<<<<< HEAD Ora proviamo a scrivere qualcosa di molto piccolo. Ad esempio, 1 microsecondo (un milionesimo di secondo): +======= +Now let's write something very small. Say, 1 microsecond (one millionth of a second): +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js let ms = 0.000001; @@ -39,7 +43,7 @@ let ms = 0.000001; Come prima, l'utilizzo di `"e"` può aiutare. Se volessimo evitare di scrivere esplicitamente tutti gli "0", potremmo scrivere: ```js -let ms = 1e-6; // six zeroes to the left from 1 +let ms = 1e-6; // six zeroes to the left from 1 ``` Se contiamo gli zeri in `0.000001`, ce ne sono 6. Quindi ovviamente `1e-6`. @@ -152,8 +156,13 @@ Ci sono due modi per farlo: alert( Math.floor(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23 ``` +<<<<<<< HEAD 2. Il metodo [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) arrotonda il numero a `n` cifre dopo la virgola e ritorna una rappresentazione in stringa del risultato. +======= +2. The method [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) rounds the number to `n` digits after the point and returns a string representation of the result. + +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let num = 12.34; alert( num.toFixed(1) ); // "12.3" @@ -170,7 +179,7 @@ Ci sono due modi per farlo: ```js run let num = 12.34; - alert( num.toFixed(5) ); // "12.34000", added zeroes to make exactly 5 digits + alert( num.toFixed(5) ); // "12.34000", added zeroes to make exactly 5 digits ``` Possiamo convertire il risultato al tipo numerico utilizzando la somma unaria o chiamando il metodo `Number()`: `+num.toFixed(5)`. @@ -182,7 +191,7 @@ Internamente, un numero è rappresentato in formato 64-bit [IEEE-754](http://en. Se un numero è troppo grande, tale da superare i 64 bit disponibili, come ad esempio un numero potenzialmente infinito: ```js run -alert( 1e500 ); // Infinity +alert( 1e500 ); // Infinity ``` Potrebbe essere poco ovvio, ma quello che accade è la perdita di precisione. @@ -193,7 +202,11 @@ Consideriamo questo test (falso!): alert( 0.1 + 0.2 == 0.3 ); // *!*false*/!* ``` +<<<<<<< HEAD Esatto, se proviamo a confrontare il risultato della somma tra `0.1` e `0.2` con `0.3`, otteniamo `false`. +======= +That's right, if we check whether the sum of `0.1` and `0.2` is `0.3`, we get `false`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Strano! Quale può essere il risultato se non `0.3`? @@ -207,7 +220,11 @@ Ma perché accade questo? Un numero viene memorizzato nella sua forma binaria, una sequenza di "1" e "0". I numeri con virgola come `0.1`, `0.2` che visti nella loro forma decimale sembrano semplici, sono in realtà una sequenza infinita di cifre nella forma binaria. +<<<<<<< HEAD In altre parole, cos'è `0.1`? Vale 1 diviso 10 `1/10`, "un decimo". Nel sistema decimale questi numeri sono facilmente rappresentabili. Prendiamo invece "un terzo": `1/3`. Diventa un numero con infiniti decimali `0.33333(3)`. +======= +In other words, what is `0.1`? It is one divided by ten `1/10`, one-tenth. In decimal numeral system such numbers are easily representable. Compare it to one-third: `1/3`. It becomes an endless fraction `0.33333(3)`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Quindi, le divisioni per potenze di `10` funzionano molto bene nel sistema decimale, non vale lo stesso con la divisione per `3`. Per la stessa ragione, nel sistema binario le divisioni per potenze di `2` sono una garanzia, ma `1/10` diventa una sequenza infinita di cifre. @@ -227,40 +244,62 @@ Questo è il motivo per cui `0.1 + 0.2` non vale esattamente `0.3`. ```smart header="Non solo JavaScript" Lo stesso problema esiste in molti altri linguaggi di programmazione. +<<<<<<< HEAD PHP, Java, C, Perl, Ruby hanno lo stesso tipo di problema, poiché si basano sullo stesso formato numerico. ``` Possiamo risolvere questo problema? Certamente, ci sono diverse soluzioni: 1. Possiamo arrotondare il risultato con un metodo [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed): +======= +PHP, Java, C, Perl, Ruby give exactly the same result, because they are based on the same numeric format. +``` - ```js run - let sum = 0.1 + 0.2; - alert( sum.toFixed(2) ); // 0.30 - ``` +Can we work around the problem? Sure, the most reliable method is to round the result with the help of a method [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed): +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb +```js run +let sum = 0.1 + 0.2; +alert( sum.toFixed(2) ); // 0.30 +``` + +<<<<<<< HEAD Da notare che `toFixed` ritorna sempre una stringa. Viene cosi garantito che ci siano almeno due cifre dopo la virgola decimale. Questo ci torna molto utile se abbiamo un e-shopping e vogliamo mostrare `$0.30`. Per tutti gli altri casi possiamo semplicemente chiamare la conversione con l'operatore di somma unaria: +======= +Please note that `toFixed` always returns a string. It ensures that it has 2 digits after the decimal point. That's actually convenient if we have an e-shopping and need to show `$0.30`. For other cases, we can use the unary plus to coerce it into a number: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb - ```js run - let sum = 0.1 + 0.2; - alert( +sum.toFixed(2) ); // 0.3 - ``` +```js run +let sum = 0.1 + 0.2; +alert( +sum.toFixed(2) ); // 0.3 +``` +<<<<<<< HEAD 2. Possiamo temporaneamente convertire i numeri ad interi per eseguire le operazioni e poi riconvertirli. In questo modo: +======= +We also can temporarily multiply the numbers by 100 (or a bigger number) to turn them into integers, do the maths, and then divide back. Then, as we're doing maths with integers, the error somewhat decreases, but we still get it on division: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb - ```js run - alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3 - ``` +```js run +alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3 +alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001 +``` +<<<<<<< HEAD Questo funziona perché quando facciamo `0.1 * 10 = 1` e `0.2 * 10 = 2` entrambi diventano interi, non vi è quindi perdita di precisione. 3. Se abbiamo a che fare con dei prezzi, la miglior soluzione rimane quella di memorizzare tutti i prezzi in centesimi, evitando quindi di utilizzare i numeri con virgola. Ma cosa succede se proviamo ad applicare uno sconto del 30%? Nella pratica, evadere completamente questo problema è difficile, in alcuni casi possono tornare utili entrambe le soluzioni spiegate sopra. +======= +So, multiply/divide approach reduces the error, but doesn't remove it totally. + +Sometimes we could try to evade fractions at all. Like if we're dealing with a shop, then we can store prices in cents instead of dollars. But what if we apply a discount of 30%? In practice, totally evading fractions is rarely possible. Just round them to cut "tails" when needed. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ````smart header="La cosa divertente" Provate ad eseguire questo: ```js run -// Hello! I'm a self-increasing number! +// Hello! I'm a self-increasing number! alert( 9999999999999999 ); // shows 10000000000000000 ``` @@ -272,7 +311,11 @@ JavaScript non mostra errori in questi casi. Semplicemente fa del suo meglio per ```smart header="Due zeri" Un'altra conseguenza divertente della rappresentazione interna è l'esistenza di due zeri: `0` e `-0`. +<<<<<<< HEAD Questo perché il segno viene rappresentato con un solo bit, in questo modo ogni numero può essere positivo o negativo, lo stesso vale per lo zero. +======= +That's because a sign is represented by a single bit, so every number can be positive or negative, including a zero. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Nella maggior parte dei casi questa differenza è impercettibile, poiché gli operatori sono studiati per trattarli allo stesso modo. ``` @@ -326,10 +369,17 @@ Da notare che una stringa vuota o contenente solo spazi viene trattata come `0` Esiste uno speciale metodo integrato [Object.is](mdn:js/Object/is) che confronta valori proprio come `===`, ma risulta molto più affidabile in due casi limite: +<<<<<<< HEAD 1. Funziona con `NaN`: `Object.is(NaN, NaN) === true`, e questo è un bene. 2. I valori `0` e `-0` sono diversi: `Object.is(0, -0) === false`, raramente ha importanza, ma questi due valori sono comunque differenti. In tutti gli altri casi, `Object.is(a, b)` equivale a `a === b`. +======= +1. It works with `NaN`: `Object.is(NaN, NaN) === true`, that's a good thing. +2. Values `0` and `-0` are different: `Object.is(0, -0) === false`, it rarely matters, but these values technically are different. + +In all other cases, `Object.is(a, b)` is the same as `a === b`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Questo metodo di confronto viene spesso utilizzato in JavaScript. Quando un algoritmo interno ha necessità di verificare che due valori siano esattamente la stessa cosa, si utilizza `Object.is` (internamente chiamato [SameValue](https://tc39.github.io/ecma262/#sec-samevalue)). ``` @@ -424,7 +474,11 @@ Per diversi sistemi numerici: Per convertire a numeri valori del tipo `12pt` e `100px`: +<<<<<<< HEAD - Usate `parseInt/parseFloat` per una conversione "leggera", che legge numeri da una stringa e ritorna il valore che è riuscito a leggere prima dell'errore. +======= +- Use `parseInt/parseFloat` for the "soft" conversion, which reads a number from a string and then returns the value they could read before the error. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Per i numeri con la virgola: @@ -433,6 +487,10 @@ Per i numeri con la virgola: Altre funzioni matematiche: +<<<<<<< HEAD - Guardate l'oggetto [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) in caso di necessità. La libreria non è molto ampia, ma è in grado di coprire le necessità di base. +======= +- See the [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) object when you need them. The library is very small, but can cover basic needs. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/05-data-types/03-string/2-check-spam/solution.md b/1-js/05-data-types/03-string/2-check-spam/solution.md index c9f4baa37..63960025d 100644 --- a/1-js/05-data-types/03-string/2-check-spam/solution.md +++ b/1-js/05-data-types/03-string/2-check-spam/solution.md @@ -1,4 +1,8 @@ +<<<<<<< HEAD Per far si che la ricerca non si preoccupi del timbro delle lettere, portiamo l'intera stringa a lettere minuscole e poi eseguiamo la ricerca: +======= +To make the search case-insensitive, let's bring the string to lower case and then search: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run function checkSpam(str) { diff --git a/1-js/05-data-types/03-string/3-truncate/solution.md b/1-js/05-data-types/03-string/3-truncate/solution.md index c7a2cb3ac..8de97cff2 100644 --- a/1-js/05-data-types/03-string/3-truncate/solution.md +++ b/1-js/05-data-types/03-string/3-truncate/solution.md @@ -2,10 +2,9 @@ La lunghezza massima deve essere `maxlength`, quindi abbiamo bisgnono di troncar Da notare che esiste un codice che identifica il simbolo "...". Quindi non vengono contati come tre punti. -```js run +```js run demo function truncate(str, maxlength) { - return (str.length > maxlength) ? + return (str.length > maxlength) ? str.slice(0, maxlength - 1) + '…' : str; } ``` - diff --git a/1-js/05-data-types/03-string/4-extract-currency/solution.md b/1-js/05-data-types/03-string/4-extract-currency/solution.md index 8b1378917..e69de29bb 100644 --- a/1-js/05-data-types/03-string/4-extract-currency/solution.md +++ b/1-js/05-data-types/03-string/4-extract-currency/solution.md @@ -1 +0,0 @@ - diff --git a/1-js/05-data-types/03-string/article.md b/1-js/05-data-types/03-string/article.md index 557412444..37a5ea364 100644 --- a/1-js/05-data-types/03-string/article.md +++ b/1-js/05-data-types/03-string/article.md @@ -548,8 +548,13 @@ Questo metodo in realtà ha due argomenti opzionali specificati nella [documenta ## Internamente, Unicode +<<<<<<< HEAD ```warn header="Apprendimento avanzato" Questa sezione andrà più in profondità riguardo le stringhe. Quello che leggerai ti potrà essere utile se hai intenzione di utilizzare emoji, simboli matematici o geroglifici. +======= +```warn header="Advanced knowledge" +The section goes deeper into string internals. This knowledge will be useful for you if you plan to deal with emoji, rare mathematical or hieroglyphic characters or other rare symbols. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Puoi semplicemente saltare questa sezione se non hai in programma di utilizzarle. ``` diff --git a/1-js/05-data-types/04-array/10-maximal-subarray/solution.md b/1-js/05-data-types/04-array/10-maximal-subarray/solution.md index 0ac86d17e..0ba80f14d 100644 --- a/1-js/05-data-types/04-array/10-maximal-subarray/solution.md +++ b/1-js/05-data-types/04-array/10-maximal-subarray/solution.md @@ -1,4 +1,8 @@ +<<<<<<< HEAD # La soluzione lenta +======= +# Slow solution +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Possiamo calcoalre tutte le somme possibili. @@ -29,8 +33,8 @@ Ad esempio, per `[-1, 2, 3, -9, 11]`: -9 -9 + 11 -// Starting from -11 --11 +// Starting from 11 +11 ``` Il codice è un ciclo annidato: il ciclo esterno cicla tutti gli elementi dell'array, quello interno esegue le somme a partire dall'elemento corrente. @@ -59,7 +63,11 @@ alert( getMaxSubSum([100, -9, 2, -3, 5]) ); // 100 La soluzione ha una complessità di [O(n2)](https://en.wikipedia.org/wiki/Big_O_notation). In altre parole, se l'array fosse 2 volte più grande, l'algoritmo lavorerebbe 4 volte più lentamente. +<<<<<<< HEAD Per grandi array (1000, 10000 o più elementi) questi algoritmi possono portare ad enormi attese. +======= +For big arrays (1000, 10000 or more items) such algorithms can lead to a serious sluggishness. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb # Soluzione performante @@ -67,7 +75,7 @@ Iniziamo ad esaminare l'array mantenendo la somma parizale degli elementi nella Se la risposta vi sembra troppo vaga, date un'occhiata al codice: -```js run +```js run demo function getMaxSubSum(arr) { let maxSum = 0; let partialSum = 0; diff --git a/1-js/05-data-types/04-array/2-create-array/solution.md b/1-js/05-data-types/04-array/2-create-array/solution.md index eec9055e7..f032b55f0 100644 --- a/1-js/05-data-types/04-array/2-create-array/solution.md +++ b/1-js/05-data-types/04-array/2-create-array/solution.md @@ -5,6 +5,6 @@ let styles = ["Jazz", "Blues"]; styles.push("Rock-n-Roll"); styles[Math.floor((styles.length - 1) / 2)] = "Classics"; alert( styles.shift() ); -styles.unshift("Rap", "Reggie"); +styles.unshift("Rap", "Reggae"); ``` diff --git a/1-js/05-data-types/04-array/array-pop.png b/1-js/05-data-types/04-array/array-pop.png index 9113c76be..4d6867b14 100644 Binary files a/1-js/05-data-types/04-array/array-pop.png and b/1-js/05-data-types/04-array/array-pop.png differ diff --git a/1-js/05-data-types/04-array/array-pop@2x.png b/1-js/05-data-types/04-array/array-pop@2x.png index e6ec8d8fb..c65ef9446 100644 Binary files a/1-js/05-data-types/04-array/array-pop@2x.png and b/1-js/05-data-types/04-array/array-pop@2x.png differ diff --git a/1-js/05-data-types/04-array/array-shift.png b/1-js/05-data-types/04-array/array-shift.png index 03b29d930..5798a6476 100644 Binary files a/1-js/05-data-types/04-array/array-shift.png and b/1-js/05-data-types/04-array/array-shift.png differ diff --git a/1-js/05-data-types/04-array/array-shift@2x.png b/1-js/05-data-types/04-array/array-shift@2x.png index c9888a44c..ba95f2651 100644 Binary files a/1-js/05-data-types/04-array/array-shift@2x.png and b/1-js/05-data-types/04-array/array-shift@2x.png differ diff --git a/1-js/05-data-types/04-array/array-speed.png b/1-js/05-data-types/04-array/array-speed.png index 3737e8248..a400d0c1d 100644 Binary files a/1-js/05-data-types/04-array/array-speed.png and b/1-js/05-data-types/04-array/array-speed.png differ diff --git a/1-js/05-data-types/04-array/array-speed@2x.png b/1-js/05-data-types/04-array/array-speed@2x.png index e45624b50..11a3e67c9 100644 Binary files a/1-js/05-data-types/04-array/array-speed@2x.png and b/1-js/05-data-types/04-array/array-speed@2x.png differ diff --git a/1-js/05-data-types/04-array/article.md b/1-js/05-data-types/04-array/article.md index 49fcd554a..22b9137c7 100644 --- a/1-js/05-data-types/04-array/article.md +++ b/1-js/05-data-types/04-array/article.md @@ -104,7 +104,11 @@ Una [queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) (coda) è Gli array supportano entrambre le operazioni. +<<<<<<< HEAD Nella pratica non è strano incontrare questo "tipo" di array. Ad esempiom una coda di messaggi che devono essere mostrati a schermo. +======= +In practice we need it very often. For example, a queue of messages that need to be shown on-screen. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Esiste un altro caso d'uso degli array -- la struttrura dati chiamata [stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)). @@ -319,7 +323,11 @@ Non è comunque un ottima idea. Si possono verificare diversi errori: Ci sono anche degli oggetti chiamati "array-like" (simili ad array) nei browser e in altri ambienti, che *assomigliano ad array*. Infatti come proprietà possiedono `length` e degli indici, ma allo stesso tempo contengono proprietà e metodi di tipo non numerico, di cui solitamente non abbiamo bisogno. Il ciclo `for..in` li passerà tutti. Quindi se stiamo utilizzando degli oggetti array-like, questi "extra" potrebbero rivelarsi un problema. +<<<<<<< HEAD 2. Il ciclo `for..in` è ottimizzato per oggetti generici, non array, può risultare quindi 10-100 volte più lento. Ovviamente rimane comunque un operazione molto veloce. Può essere un problema solo in caso si verifichino ingorghi. +======= +2. The `for..in` loop is optimized for generic objects, not arrays, and thus is 10-100 times slower. Of course, it's still very fast. The speedup may only matter in bottlenecks or seem irrelevant. But still we should be aware of the difference. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Generalmente, non dovremmo utilizzare `for..in` per array. diff --git a/1-js/05-data-types/04-array/queue.png b/1-js/05-data-types/04-array/queue.png index 5e1fb640c..39af3beda 100644 Binary files a/1-js/05-data-types/04-array/queue.png and b/1-js/05-data-types/04-array/queue.png differ diff --git a/1-js/05-data-types/04-array/queue@2x.png b/1-js/05-data-types/04-array/queue@2x.png index 6acfc83d1..75045a51d 100644 Binary files a/1-js/05-data-types/04-array/queue@2x.png and b/1-js/05-data-types/04-array/queue@2x.png differ diff --git a/1-js/05-data-types/04-array/stack.png b/1-js/05-data-types/04-array/stack.png index d1c9cb9af..7d2599355 100644 Binary files a/1-js/05-data-types/04-array/stack.png and b/1-js/05-data-types/04-array/stack.png differ diff --git a/1-js/05-data-types/04-array/stack@2x.png b/1-js/05-data-types/04-array/stack@2x.png index b3835fa44..16c622518 100644 Binary files a/1-js/05-data-types/04-array/stack@2x.png and b/1-js/05-data-types/04-array/stack@2x.png differ diff --git a/1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js b/1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js index 024d6d6c2..490f570ad 100644 --- a/1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js +++ b/1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js @@ -1,8 +1,10 @@ function camelize(str) { return str - .split('-') // my-long-word -> ['my', 'long', 'word'] - .map( + .split('-') // splits 'my-long-word' into array ['my', 'long', 'word'] + .map( + // capitalizes first letters of all array items except the first one + // converts ['my', 'long', 'word'] into ['my', 'Long', 'Word'] (word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1) - ) // ['my', 'long', 'word'] -> ['my', 'Long', 'Word'] - .join(''); // ['my', 'Long', 'Word'] -> myLongWord + ) + .join(''); // joins ['my', 'Long', 'Word'] into 'myLongWord' } diff --git a/1-js/05-data-types/05-array-methods/11-array-unique/solution.md b/1-js/05-data-types/05-array-methods/11-array-unique/solution.md index 247d96423..5179d52f0 100644 --- a/1-js/05-data-types/05-array-methods/11-array-unique/solution.md +++ b/1-js/05-data-types/05-array-methods/11-array-unique/solution.md @@ -2,7 +2,7 @@ Attraversiamo gli elementi dell'array: - Per ogni elemento controlliamo se l'array risultante già lo contiene. - Se lo troviamo, passiamo al prossimo, altrimenti lo aggiungiamo. -```js run +```js run demo function unique(arr) { let result = []; diff --git a/1-js/05-data-types/05-array-methods/2-filter-range/solution.md b/1-js/05-data-types/05-array-methods/2-filter-range/solution.md index e69de29bb..73993a07a 100644 --- a/1-js/05-data-types/05-array-methods/2-filter-range/solution.md +++ b/1-js/05-data-types/05-array-methods/2-filter-range/solution.md @@ -0,0 +1,14 @@ +```js run demo +function filterRange(arr, a, b) { + // added brackets around the expression for better readability + return arr.filter(item => (a <= item && item <= b)); +} + +let arr = [5, 3, 8, 1]; + +let filtered = filterRange(arr, 1, 4); + +alert( filtered ); // 3,1 (matching values) + +alert( arr ); // 5,3,8,1 (not modified) +``` diff --git a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js index 61cda126b..488db3755 100644 --- a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js +++ b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js @@ -1,5 +1,4 @@ - function filterRangeInPlace(arr, a, b) { for (let i = 0; i < arr.length; i++) { @@ -12,4 +11,4 @@ function filterRangeInPlace(arr, a, b) { } } -} \ No newline at end of file +} diff --git a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md index e69de29bb..36e3130ff 100644 --- a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md +++ b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md @@ -0,0 +1,21 @@ +```js run demo +function filterRangeInPlace(arr, a, b) { + + for (let i = 0; i < arr.length; i++) { + let val = arr[i]; + + // remove if outside of the interval + if (val < a || val > b) { + arr.splice(i, 1); + i--; + } + } + +} + +let arr = [5, 3, 8, 1]; + +filterRangeInPlace(arr, 1, 4); // removed the numbers except from 1 to 4 + +alert( arr ); // [3, 1] +``` diff --git a/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md b/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md index 8d56db9d6..9f1ade707 100644 --- a/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md +++ b/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md @@ -1,17 +1,18 @@ ```js run no-beautify -function sortByName(arr) { - arr.sort((a, b) => a.name > b.name); +function sortByAge(arr) { + arr.sort((a, b) => a.age > b.age ? 1 : -1); } let john = { name: "John", age: 25 }; let pete = { name: "Pete", age: 30 }; let mary = { name: "Mary", age: 28 }; -let arr = [ john, pete, mary ]; +let arr = [ pete, john, mary ]; -sortByName(arr); +sortByAge(arr); // now sorted is: [john, mary, pete] +alert(arr[0].name); // John alert(arr[1].name); // Mary +alert(arr[2].name); // Pete ``` - diff --git a/1-js/05-data-types/05-array-methods/8-sort-objects/task.md b/1-js/05-data-types/05-array-methods/8-sort-objects/task.md index 74e232482..dd1569133 100644 --- a/1-js/05-data-types/05-array-methods/8-sort-objects/task.md +++ b/1-js/05-data-types/05-array-methods/8-sort-objects/task.md @@ -2,9 +2,15 @@ importance: 5 --- +<<<<<<< HEAD # Riordinare oggetti Scrivete una funzione `sortByName(users)` che prenda un array di oggetti con proprietà `name` e lo riordini. +======= +# Sort users by age + +Write the function `sortByAge(users)` that gets an array of objects with the `age` property and sorts them by `age`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -13,11 +19,12 @@ let john = { name: "John", age: 25 }; let pete = { name: "Pete", age: 30 }; let mary = { name: "Mary", age: 28 }; -let arr = [ john, pete, mary ]; +let arr = [ pete, john, mary ]; -sortByName(arr); +sortByAge(arr); // now: [john, mary, pete] +alert(arr[0].name); // John alert(arr[1].name); // Mary +alert(arr[2].name); // Pete ``` - diff --git a/1-js/05-data-types/05-array-methods/article.md b/1-js/05-data-types/05-array-methods/article.md index 549fc5696..f1d05535a 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -122,7 +122,11 @@ La sintassi è: arr.slice(start, end) ``` +<<<<<<< HEAD Ritorna un nuovo array contente tutti gli elementi a partire da `"start"` fino ad `"end"` (`"end"` esclusa). Sia `start` che `end` possono essere negativi, in tal caso si inizierà a contare dalla coda dell'array. +======= +It returns a new array containing all items from index `"start"` to `"end"` (not including `"end"`). Both `start` and `end` can be negative, in that case position from array end is assumed. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Funziona come `str.slice`, ma crea dei sotto-array piuttosto che sotto-stringhe. @@ -201,7 +205,40 @@ let arrayLike = { alert( arr.concat(arrayLike) ); // 1,2,something,else ``` +<<<<<<< HEAD ## Ricerca in array +======= +## Iterate: forEach + +The [arr.forEach](mdn:js/Array/forEach) method allows to run a function for every element of the array. + +The syntax: +```js +arr.forEach(function(item, index, array) { + // ... do something with item +}); +``` + +For instance, this shows each element of the array: + +```js run +// for each element call alert +["Bilbo", "Gandalf", "Nazgul"].forEach(alert); +``` + +And this code is more elaborate about their positions in the target array: + +```js run +["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => { + alert(`${item} is at index ${index} in ${array}`); +}); +``` + +The result of the function (if it returns any) is thrown away and ignored. + + +## Searching in array +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ora vedremo dei metodi per effettuare ricerche in un array. @@ -246,7 +283,8 @@ In questi casi si utilizza il metodo [arr.find](mdn:js/Array/find). La sintassi è: ```js let result = arr.find(function(item, index, array) { - // should return true if the item is what we are looking for + // if true is returned, item is returned and iteration is stopped + // for falsy scenario returns undefined }); ``` @@ -274,9 +312,15 @@ alert(user.name); // John Nella realtà gli array di oggetti sono una cosa molto comune, quindi il metodo `find` risulta molto utile. +<<<<<<< HEAD Da notare che nell'esempio noi forniamo a `find` un singolo argomento `item => item.id == 1`. Gli altri parametri di `find` sono raramente utilizzati. Il metodo [arr.findIndex](mdn:js/Array/findIndex) fa essenzialmente la stessa cosa, semplicemente ritorna l'indice in cui è stata trovata la corrispondenza piuttosto di ritornare l'oggetto stesso. +======= +Note that in the example we provide to `find` the function `item => item.id == 1` with one argument. Other arguments of this function are rarely used. + +The [arr.findIndex](mdn:js/Array/findIndex) method is essentially the same, but it returns the index where the element was found instead of the element itself and `-1` is returned when nothing is found. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ### filter @@ -284,11 +328,16 @@ Il metodo `find` cerca un singola occorrenza dell'elemento (la prima) e ritorna Se vogliamo cercare più occorrenze, possiamo utilizzare [arr.filter(fn)](mdn:js/Array/filter). +<<<<<<< HEAD La sintassi è pressoché la stessa di `find`, ma ritorna un array contenente tutte le corrispondenze trovate: +======= +The syntax is similar to `find`, but filter continues to iterate for all array elements even if `true` is already returned: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js let results = arr.filter(function(item, index, array) { - // should return true if the item passes the filter + // if true item is pushed to results and iteration continues + // returns empty array for complete falsy scenario }); ``` @@ -328,7 +377,7 @@ La funzione viene chiamata per ogni elemento dell'array e ritorna un array di ri Ad esempio, qui trasformiamo ogni elemento nella sua lunghezza: ```js run -let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length) +let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length); alert(lengths); // 5,7,6 ``` @@ -476,7 +525,11 @@ alert( str.split('') ); // t,e,s,t ``` ```` +<<<<<<< HEAD La chiamata [arr.join(str)](mdn:js/Array/join) fa esattamente l'inverso di `split`. Crea una stringa con gli elementi di `arr` divisi da un carattere di tipo stringa `str`. +======= +The call [arr.join(separator)](mdn:js/Array/join) does the reverse to `split`. It creates a string of `arr` items glued by `separator` between them. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: @@ -491,23 +544,35 @@ alert( str ); // Bilbo;Gandalf;Nazgul ### reduce/reduceRight Quando vogliamo iterare su un array -- possiamo utilizzare `forEach`. +<<<<<<< HEAD Quando invece abbiamo la necessità di iterare e ritornare dati per ogni elemento -- possiamo usare `map`. +======= +When we need to iterate over an array -- we can use `forEach`, `for` or `for..of`. + +When we need to iterate and return the data for each element -- we can use `map`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb I metodi [arr.reduce](mdn:js/Array/reduce) e [arr.reduceRight](mdn:js/Array/reduceRight) fanno parte della stessa categoria, ma sono leggermente più complessi. Vengono utilizzati per calcolare un singolo valore basato sul contenuto dell'array. La sintassi è: ```js -let value = arr.reduce(function(previousValue, item, index, arr) { +let value = arr.reduce(function(previousValue, item, index, array) { // ... }, initial); ``` La funzione viene applicata agli elementi. Potrete notare che fra gli argomenti ce ne sono alcuni di familiari, a partire dal secondo: +<<<<<<< HEAD - `item` -- è l'elemento corrente. - `index` -- è la sua posizione. - `arr` -- è l'array. +======= +- `item` -- is the current array item. +- `index` -- is its position. +- `array` -- is the array. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Finora, è uguale a `forEach/map`. Ma c'è un ulteriore argomento: @@ -537,7 +602,11 @@ Il flusso di calcolo: ![](reduce.png) +<<<<<<< HEAD O nella forma tabellare, in cui ogni riga rappresenta una chiamata di funzione: +======= +Or in the form of a table, where each row represents a function call on the next array element: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb | |`sum`|`current`|`result`| |---|-----|---------|---------| @@ -583,6 +652,7 @@ Quindi è fortemente consigliato di specificare sempre un valore iniziale. Il metodo [arr.reduceRight](mdn:js/Array/reduceRight) fa esattamente la stessa cosa, ma da destra verso sinistra. +<<<<<<< HEAD ## Iterate: forEach Il metodo [arr.forEach](mdn:js/Array/forEach) consente di eseguire una funzione per ogni elemento dell'array. @@ -611,6 +681,8 @@ Questo codice è più elaborato e mostra anche la posizione: Il risultato della funzione (sempre se ritorna qualcosa) viene ignorato. +======= +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Array.isArray Gli array non utilizzano una sintassi differente. Sono comunque basati sugli oggetti. @@ -687,11 +759,22 @@ Un breve riepilogo dei metodi per array: - `slice(start, end)` -- crea un nuovo array, e copia al suo interno gli elementi da `start` fino ad `end` (esclusa). - `concat(...items)` -- ritorna un nuovo array: copia tutti gli elementi di quello corrente e ci aggiunge `items`. Se uno degli `items` è un array, allora vengono presi anche i suoi elementi. +<<<<<<< HEAD - Ricercare elementi: - `indexOf/lastIndexOf(item, pos)` -- cerca `item` a partire da `pos`, e ritorna l'indice, oppure `-1` se non lo trova. - `includes(value)` -- ritorna `true` se l'array contiene `value`, altrimenti `false`. - `find/filter(func)` -- filtra gli elementi tramite la funzione, ritorna il primo/tutti i valori che ritornano `true`. - `findIndex` è simile a `find`, ma ritorna l'indice piuttosto del valore. +======= +- To search among elements: + - `indexOf/lastIndexOf(item, pos)` -- look for `item` starting from position `pos`, return the index or `-1` if not found. + - `includes(value)` -- returns `true` if the array has `value`, otherwise `false`. + - `find/filter(func)` -- filter elements through the function, return first/all values that make it return `true`. + - `findIndex` is like `find`, but returns the index instead of a value. + +- To iterate over elements: + - `forEach(func)` -- calls `func` for every element, does not return anything. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb - Per modificare un array: - `map(func)` -- crea un nuovo array con i risultati della chiamata `func` su tutti i suoi elementi. @@ -700,11 +783,16 @@ Un breve riepilogo dei metodi per array: - `split/join` -- converte una stringa in array e vice versa. - `reduce(func, initial)` -- calculate a single value over the array by calling `func` for each element and passing an intermediate result between the calls. +<<<<<<< HEAD - Per iterare sugli elementi: - `forEach(func)` -- chiama `func` su ogni elemento, ma non ritorna nulla. - Un altro metodo utile: - `Array.isArray(arr)` controlla se `arr` è un array. +======= +- Additionally: + - `Array.isArray(arr)` checks `arr` for being an array. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Da notare che i metodi `sort`, `reverse` e `splice` modificano l'array stesso. diff --git a/1-js/05-data-types/05-array-methods/reduce.png b/1-js/05-data-types/05-array-methods/reduce.png index 41476d2ae..7566c4d84 100644 Binary files a/1-js/05-data-types/05-array-methods/reduce.png and b/1-js/05-data-types/05-array-methods/reduce.png differ diff --git a/1-js/05-data-types/05-array-methods/reduce@2x.png b/1-js/05-data-types/05-array-methods/reduce@2x.png index f31647d17..7c9fd6692 100644 Binary files a/1-js/05-data-types/05-array-methods/reduce@2x.png and b/1-js/05-data-types/05-array-methods/reduce@2x.png differ diff --git a/1-js/05-data-types/07-map-set-weakmap-weakset/02-filter-anagrams/solution.md b/1-js/05-data-types/07-map-set-weakmap-weakset/02-filter-anagrams/solution.md index 0e841a9a2..236fa224c 100644 --- a/1-js/05-data-types/07-map-set-weakmap-weakset/02-filter-anagrams/solution.md +++ b/1-js/05-data-types/07-map-set-weakmap-weakset/02-filter-anagrams/solution.md @@ -59,7 +59,7 @@ Qui potremmo anche utilizzare un normale oggetto piuttosto di `Map`, poiché le Questo è un esempio di possibile soluzione: -```js run +```js run demo function aclean(arr) { let obj = {}; @@ -68,7 +68,7 @@ function aclean(arr) { obj[sorted] = arr[i]; } - return Array.from(Object.values(obj)); + return Object.values(obj); } let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"]; diff --git a/1-js/05-data-types/07-map-set-weakmap-weakset/03-iterable-keys/task.md b/1-js/05-data-types/07-map-set-weakmap-weakset/03-iterable-keys/task.md index c4aec4624..6724d0f3a 100644 --- a/1-js/05-data-types/07-map-set-weakmap-weakset/03-iterable-keys/task.md +++ b/1-js/05-data-types/07-map-set-weakmap-weakset/03-iterable-keys/task.md @@ -16,7 +16,7 @@ map.set("name", "John"); let keys = map.keys(); *!* -// Error: numbers.push is not a function +// Error: keys.push is not a function keys.push("more"); */!* ``` diff --git a/1-js/05-data-types/07-map-set-weakmap-weakset/article.md b/1-js/05-data-types/07-map-set-weakmap-weakset/article.md index 17ea91368..fc039a140 100644 --- a/1-js/05-data-types/07-map-set-weakmap-weakset/article.md +++ b/1-js/05-data-types/07-map-set-weakmap-weakset/article.md @@ -159,6 +159,7 @@ L'iterazione segue lo stesso ordine in cui sono stati inseriti i valori. `Map` c Inoltre, `Map` possiede un metodo integrato `forEach`, simile agli `Array`: ```js +// runs the function for each (key, value) pair recipeMap.forEach( (value, key, map) => { alert(`${key}: ${value}`); // cucumber: 500 etc }); @@ -327,7 +328,7 @@ Fate il confronto con l'esempio di `Map` sopra. Ora se `john` esiste solo come c - `weakMap.get(key)` - `weakMap.set(key, value)` -- `weakMap.delete(key, value)` +- `weakMap.delete(key)` - `weakMap.has(key)` Perché questa limitazione? Per ragioni tecniche. Se un oggetto ha perso tutti i riferimenti (come `john` nel codice sopra), allora verrà automaticamente eliminato. Ma tecnicamente non è specificato esattamente quando *averrà la pulizia*. diff --git a/1-js/05-data-types/08-keys-values-entries/01-sum-salaries/solution.md b/1-js/05-data-types/08-keys-values-entries/01-sum-salaries/solution.md index e69de29bb..27a7b418a 100644 --- a/1-js/05-data-types/08-keys-values-entries/01-sum-salaries/solution.md +++ b/1-js/05-data-types/08-keys-values-entries/01-sum-salaries/solution.md @@ -0,0 +1,29 @@ +```js run demo +function sumSalaries(salaries) { + + let sum = 0; + for (let salary of Object.values(salaries)) { + sum += salary; + } + + return sum; // 650 +} + +let salaries = { + "John": 100, + "Pete": 300, + "Mary": 250 +}; + +alert( sumSalaries(salaries) ); // 650 +``` +Or, optionally, we could also get the sum using `Object.values` and `reduce`: + +```js +// reduce loops over array of salaries, +// adding them up +// and returns the result +function sumSalaries(salaries) { + return Object.values(salaries).reduce((a, b) => a + b, 0) // 650 +} +``` diff --git a/1-js/05-data-types/08-keys-values-entries/article.md b/1-js/05-data-types/08-keys-values-entries/article.md index 8a3865887..8ba096d3c 100644 --- a/1-js/05-data-types/08-keys-values-entries/article.md +++ b/1-js/05-data-types/08-keys-values-entries/article.md @@ -45,7 +45,7 @@ let user = { }; ``` -- `Object.keys(user) = [name, age]` +- `Object.keys(user) = ["name", "age"]` - `Object.values(user) = ["John", 30]` - `Object.entries(user) = [ ["name","John"], ["age",30] ]` diff --git a/1-js/05-data-types/09-destructuring-assignment/6-max-salary/_js.view/solution.js b/1-js/05-data-types/09-destructuring-assignment/6-max-salary/_js.view/solution.js index d95cf1b1e..f4bd5c761 100644 --- a/1-js/05-data-types/09-destructuring-assignment/6-max-salary/_js.view/solution.js +++ b/1-js/05-data-types/09-destructuring-assignment/6-max-salary/_js.view/solution.js @@ -3,7 +3,7 @@ function topSalary(salaries) { let max = 0; let maxName = null; - for(let [name, salary] of Object.entries(salaries)) { + for(const [name, salary] of Object.entries(salaries)) { if (max < salary) { max = salary; maxName = name; diff --git a/1-js/05-data-types/09-destructuring-assignment/article.md b/1-js/05-data-types/09-destructuring-assignment/article.md index 116f0dd15..d58a3a26d 100644 --- a/1-js/05-data-types/09-destructuring-assignment/article.md +++ b/1-js/05-data-types/09-destructuring-assignment/article.md @@ -43,19 +43,28 @@ let surname = arr[1]; ``` ```` +<<<<<<< HEAD ````smart header="Ignora i primi elementi" Possono essere ignorati degli elementi dell'array inserendo una virgola: +======= +````smart header="Ignore elements using commas" +Unwanted elements of the array can also be thrown away via an extra comma: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run *!* -// first and second elements are not needed -let [, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; +// second element is not needed +let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; */!* alert( title ); // Consul ``` +<<<<<<< HEAD Nel codice sopra, il primo e secondo elemento vengono saltati, il terzo viene assegnato a `title`, il resto dell'array viene ignorato. +======= +In the code above, the second element of the array is skipped, the third one is assigned to `title`, and the rest of the array is also skipped. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```` ````smart header="Funziona con qualsiasi itarabile" @@ -129,6 +138,7 @@ alert(name1); // Julius alert(name2); // Caesar *!* +// Note that type of `rest` is Array. alert(rest[0]); // Consul alert(rest[1]); // of the Roman Republic alert(rest.length); // 2 @@ -147,6 +157,7 @@ let [firstName, surname] = []; */!* alert(firstName); // undefined +alert(surname); // undefined ``` Se volessimo utilizzare un nostro valore di "default", potremmo fornirlo con la sintassi `=`: @@ -336,7 +347,11 @@ Il problema è che JavaScript tratta `{...}` come un blocco di codice. Questo bl } ``` +<<<<<<< HEAD Per informare JavaScript che non ci troviamo in un blocco di codice, possiamo raggruppare l'intera assegnazione tra parentesi `(...)`: +======= +To show JavaScript that it's not a code block, we can wrap the whole assignment in parentheses `(...)`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let title, width, height; @@ -384,6 +399,8 @@ alert(item2); // Donut L'intero oggetto `options` ad eccezione di `extra` il quale non viene menzionato, viene assegnato alle corrispondenti variabili. +Note that `size` and `items` itself is not destructured. + ![](destructuring-complex.png) Infine, abbiamo `width`, `height`, `item1`, `item2` e `title` che assumo il valore di default. diff --git a/1-js/05-data-types/09-destructuring-assignment/destructuring-complex.png b/1-js/05-data-types/09-destructuring-assignment/destructuring-complex.png index 50c4ffc93..c46bf6e45 100644 Binary files a/1-js/05-data-types/09-destructuring-assignment/destructuring-complex.png and b/1-js/05-data-types/09-destructuring-assignment/destructuring-complex.png differ diff --git a/1-js/05-data-types/09-destructuring-assignment/destructuring-complex@2x.png b/1-js/05-data-types/09-destructuring-assignment/destructuring-complex@2x.png index bb908281d..26032a3d6 100644 Binary files a/1-js/05-data-types/09-destructuring-assignment/destructuring-complex@2x.png and b/1-js/05-data-types/09-destructuring-assignment/destructuring-complex@2x.png differ diff --git a/1-js/05-data-types/10-date/2-get-week-day/solution.md b/1-js/05-data-types/10-date/2-get-week-day/solution.md index 479951fd6..306c58935 100644 --- a/1-js/05-data-types/10-date/2-get-week-day/solution.md +++ b/1-js/05-data-types/10-date/2-get-week-day/solution.md @@ -2,7 +2,7 @@ Il metodo `date.getDay()` ritorna il numero del giorno della settimana, comincia Creiamo quindi un array di giorni della settimana, che utilizzeremo per assegnare il numero della settimana al giorno corretto: -```js run +```js run demo function getWeekDay(date) { let days = ['DOM', 'LUN', 'MAR', 'MER', 'GIO', 'VEN', 'SAB']; diff --git a/1-js/05-data-types/10-date/3-weekday/solution.md b/1-js/05-data-types/10-date/3-weekday/solution.md index bfe3f4cac..e69de29bb 100644 --- a/1-js/05-data-types/10-date/3-weekday/solution.md +++ b/1-js/05-data-types/10-date/3-weekday/solution.md @@ -1,14 +0,0 @@ -```js run -function getLocalDay(date) { - - let day = date.getDay(); - - if (day == 0) { // 0 becomes 7 - day = 7; - } - - return day; -} - -alert( getLocalDay(new Date(2012, 0, 3)) ); // 2 -``` diff --git a/1-js/05-data-types/10-date/4-get-date-ago/solution.md b/1-js/05-data-types/10-date/4-get-date-ago/solution.md index e177c14ad..e174330e7 100644 --- a/1-js/05-data-types/10-date/4-get-date-ago/solution.md +++ b/1-js/05-data-types/10-date/4-get-date-ago/solution.md @@ -11,7 +11,7 @@ function getDateAgo(date, days) { Per implementarlo correttamente dovremmo clonare l'oggetto, come nel codice seguente: -```js run +```js run demo function getDateAgo(date, days) { let dateCopy = new Date(date); diff --git a/1-js/05-data-types/10-date/5-last-day-of-month/solution.md b/1-js/05-data-types/10-date/5-last-day-of-month/solution.md index 6287abbd3..1c01d1cbd 100644 --- a/1-js/05-data-types/10-date/5-last-day-of-month/solution.md +++ b/1-js/05-data-types/10-date/5-last-day-of-month/solution.md @@ -1,5 +1,10 @@ +<<<<<<< HEAD Creiamo un oggetto `date` con il mese successivo, e come giorno passiamo zero: ```js run +======= +Let's create a date using the next month, but pass zero as the day: +```js run demo +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb function getLastDayOfMonth(year, month) { let date = new Date(year, month + 1, 0); return date.getDate(); diff --git a/1-js/05-data-types/10-date/6-get-seconds-today/solution.md b/1-js/05-data-types/10-date/6-get-seconds-today/solution.md index 74423510c..25a229a7b 100644 --- a/1-js/05-data-types/10-date/6-get-seconds-today/solution.md +++ b/1-js/05-data-types/10-date/6-get-seconds-today/solution.md @@ -22,5 +22,5 @@ Una soluzione alternativa potrebbe essere quella di ottenere ore/minuti/secondi function getSecondsToday() { let d = new Date(); return d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds(); -}; +} ``` diff --git a/1-js/05-data-types/10-date/8-format-date-relative/solution.md b/1-js/05-data-types/10-date/8-format-date-relative/solution.md index a569e354f..8ba39e8f0 100644 --- a/1-js/05-data-types/10-date/8-format-date-relative/solution.md +++ b/1-js/05-data-types/10-date/8-format-date-relative/solution.md @@ -1,6 +1,6 @@ Per ottenere il tempo passato da `date` fino ad ora -- sottraiamo le date. -```js run +```js run demo function formatDate(date) { let diff = new Date() - date; // the difference in milliseconds @@ -57,12 +57,12 @@ function formatDate(date) { let diffSec = Math.round(diffMs / 1000); let diffMin = diffSec / 60; let diffHour = diffMin / 60; - + // formatting year = year.toString().slice(-2); month = month < 10 ? '0' + month : month; dayOfMonth = dayOfMonth < 10 ? '0' + dayOfMonth : dayOfMonth; - + if (diffSec < 1) { return 'right now'; } else if (diffMin < 1) { diff --git a/1-js/05-data-types/10-date/article.md b/1-js/05-data-types/10-date/article.md index efa021ae4..1dd3eab0c 100644 --- a/1-js/05-data-types/10-date/article.md +++ b/1-js/05-data-types/10-date/article.md @@ -2,7 +2,11 @@ Ora analizziamo un nuovo oggetto integrato: [Date](mdn:js/Date). Che memorizza la data, l'ora e fornisce dei metodi utili per il trattamento della data/ora. +<<<<<<< HEAD Ad esempio, possiamo utilizzarlo per memorizzare modifiche su orari, o per misurare il tempo, o solamente per ottenere l'informazione della data corrente. +======= +For instance, we can use it to store creation/modification times, to measure time, or just to print out the current date. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ## Creazione @@ -39,7 +43,13 @@ Per creare un nuovo oggetto `Date`, chiamiamo `new Date()` con uno dei seguenti ```js run let date = new Date("2017-01-26"); - alert(date); // Thu Jan 26 2017 ... + alert(date); + // The time portion of the date is assumed to be midnight GMT and + // is adjusted according to the timezone the code is run in + // So the result could be + // Thu Jan 26 2017 11:00:00 GMT+1100 (Australian Eastern Daylight Time) + // or + // Wed Jan 25 2017 16:00:00 GMT-0800 (Pacific Standard Time) ``` `new Date(year, month, date, hours, minutes, seconds, ms)` @@ -108,8 +118,12 @@ alert( date.getHours() ); alert( date.getUTCHours() ); ``` +<<<<<<< HEAD Oltre ai metodi forniti, ce ne sono altri due di speciali, che non possiedono la variante UTC: +======= +Besides the given methods, there are two special ones that do not have a UTC-variant: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb [getTime()](mdn:js/Date/getTime) : Ritorna il timestamp della data -- il numero di millisecondi trascorsi dal 1 Gennaio 1970 in UTC+0. @@ -420,4 +434,8 @@ alert(`Loading started ${performance.now()}ms ago`); // more than 3 digits after the decimal point are precision errors, but only the first 3 are correct ``` +<<<<<<< HEAD Node.JS possiede un modulo `microtime` e altri metodi. Tecnicamente, la maggior parte degli ambienti forniscono un modo per gestire precisioni più elevate, questo non è pero previsto dall'oggetto `Date`. +======= +Node.js has `microtime` module and other ways. Technically, any device and environment allows to get more precision, it's just not in `Date`. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb diff --git a/1-js/05-data-types/11-json/article.md b/1-js/05-data-types/11-json/article.md index 07c4bc49d..d6ec47a28 100644 --- a/1-js/05-data-types/11-json/article.md +++ b/1-js/05-data-types/11-json/article.md @@ -359,7 +359,11 @@ alert( JSON.stringify(meetup) ); Qui possiamo vedere che `date` `(1)` diventa una stringa. Questo accade perché tutti gi oggetti di tipo `Date` possiedono un metodo `toJSON`. +<<<<<<< HEAD Ora proviamo ad aggiungere un metodo `toJSON` personalizzato per il nostro oggetto `room`: +======= +Now let's add a custom `toJSON` for our object `room` `(2)`: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run let room = { diff --git a/1-js/05-data-types/11-json/json-meetup.png b/1-js/05-data-types/11-json/json-meetup.png index 0a26e0a67..268666424 100644 Binary files a/1-js/05-data-types/11-json/json-meetup.png and b/1-js/05-data-types/11-json/json-meetup.png differ diff --git a/1-js/05-data-types/11-json/json-meetup@2x.png b/1-js/05-data-types/11-json/json-meetup@2x.png index b5f6a4012..a8f2cd609 100644 Binary files a/1-js/05-data-types/11-json/json-meetup@2x.png and b/1-js/05-data-types/11-json/json-meetup@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.png b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.png index c45418ff4..d0d37e35e 100644 Binary files a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.png and b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.png differ diff --git a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree@2x.png b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree@2x.png index 6fc39ae13..3a937c64c 100644 Binary files a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree@2x.png and b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list-0.png b/1-js/06-advanced-functions/01-recursion/linked-list-0.png index 000a80da8..c694e7021 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list-0.png and b/1-js/06-advanced-functions/01-recursion/linked-list-0.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list-0@2x.png b/1-js/06-advanced-functions/01-recursion/linked-list-0@2x.png index 5a2368694..1fe50ace4 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list-0@2x.png and b/1-js/06-advanced-functions/01-recursion/linked-list-0@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list-remove-1.png b/1-js/06-advanced-functions/01-recursion/linked-list-remove-1.png index 477989ad8..b4c89ecdf 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list-remove-1.png and b/1-js/06-advanced-functions/01-recursion/linked-list-remove-1.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list-remove-1@2x.png b/1-js/06-advanced-functions/01-recursion/linked-list-remove-1@2x.png index 41de7661c..6b5b95a0f 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list-remove-1@2x.png and b/1-js/06-advanced-functions/01-recursion/linked-list-remove-1@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list-split.png b/1-js/06-advanced-functions/01-recursion/linked-list-split.png index ac2203490..310a36066 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list-split.png and b/1-js/06-advanced-functions/01-recursion/linked-list-split.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list-split@2x.png b/1-js/06-advanced-functions/01-recursion/linked-list-split@2x.png index 201c66f15..2de39ca4f 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list-split@2x.png and b/1-js/06-advanced-functions/01-recursion/linked-list-split@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list.png b/1-js/06-advanced-functions/01-recursion/linked-list.png index 64b6fb2b7..80fabffc6 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list.png and b/1-js/06-advanced-functions/01-recursion/linked-list.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list@2x.png b/1-js/06-advanced-functions/01-recursion/linked-list@2x.png index c28fa8259..1e6dc1483 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list@2x.png and b/1-js/06-advanced-functions/01-recursion/linked-list@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/recursion-pow.png b/1-js/06-advanced-functions/01-recursion/recursion-pow.png index 30577f89c..354f01271 100644 Binary files a/1-js/06-advanced-functions/01-recursion/recursion-pow.png and b/1-js/06-advanced-functions/01-recursion/recursion-pow.png differ diff --git a/1-js/06-advanced-functions/01-recursion/recursion-pow@2x.png b/1-js/06-advanced-functions/01-recursion/recursion-pow@2x.png index c19973420..22ae94410 100644 Binary files a/1-js/06-advanced-functions/01-recursion/recursion-pow@2x.png and b/1-js/06-advanced-functions/01-recursion/recursion-pow@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/recursive-salaries.png b/1-js/06-advanced-functions/01-recursion/recursive-salaries.png index 2b9015409..b40783a9b 100644 Binary files a/1-js/06-advanced-functions/01-recursion/recursive-salaries.png and b/1-js/06-advanced-functions/01-recursion/recursive-salaries.png differ diff --git a/1-js/06-advanced-functions/01-recursion/recursive-salaries@2x.png b/1-js/06-advanced-functions/01-recursion/recursive-salaries@2x.png index 261ab144e..e37597128 100644 Binary files a/1-js/06-advanced-functions/01-recursion/recursive-salaries@2x.png and b/1-js/06-advanced-functions/01-recursion/recursive-salaries@2x.png differ diff --git a/1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md b/1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md index 0374c6b87..878dfd8d9 100644 --- a/1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md +++ b/1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md @@ -61,8 +61,13 @@ function showName(firstName, lastName, ...titles) { showName("Julius", "Caesar", "Consul", "Imperator"); ``` +<<<<<<< HEAD ````warn header="I parametri resto devono apparire alla fine" I parametri resto raccolgono tutti gli argomenti che avanzano, quindi non avrebbe senso fare: +======= +````warn header="The rest parameters must be at the end" +The rest parameters gather all remaining arguments, so the following does not make sense and causes an error: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js function f(arg1, ...rest, arg2) { // arg2 after ...rest ?! diff --git a/1-js/06-advanced-functions/03-closure/4-closure-sum/solution.md b/1-js/06-advanced-functions/03-closure/4-closure-sum/solution.md index 28e3614a0..a741b8945 100644 --- a/1-js/06-advanced-functions/03-closure/4-closure-sum/solution.md +++ b/1-js/06-advanced-functions/03-closure/4-closure-sum/solution.md @@ -1,4 +1,8 @@ +<<<<<<< HEAD Perchè la seconda parentesi funzioni, la prima deve ritornare una funzione. +======= +For the second parentheses to work, the first ones must return a function. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Come in questo esempio: diff --git a/1-js/06-advanced-functions/03-closure/4-closure-sum/task.md b/1-js/06-advanced-functions/03-closure/4-closure-sum/task.md index a95063e8e..f21a22ad3 100644 --- a/1-js/06-advanced-functions/03-closure/4-closure-sum/task.md +++ b/1-js/06-advanced-functions/03-closure/4-closure-sum/task.md @@ -6,7 +6,11 @@ importance: 4 Scrivete la funzione `sum` che funziona in questo modo: `sum(a)(b) = a+b`. +<<<<<<< HEAD Esattamente, due parentesi tonde (non è un errore). +======= +Yes, exactly this way, using double parentheses (not a mistype). +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio: diff --git a/1-js/06-advanced-functions/03-closure/6-filter-through-function/solution.md b/1-js/06-advanced-functions/03-closure/6-filter-through-function/solution.md index 5bbc33b02..46c5514a8 100644 --- a/1-js/06-advanced-functions/03-closure/6-filter-through-function/solution.md +++ b/1-js/06-advanced-functions/03-closure/6-filter-through-function/solution.md @@ -14,7 +14,7 @@ alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6 # Filter inArray -```js run +```js run demo function inArray(arr) { return function(x) { return arr.includes(x); diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.png b/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.png index d51e8167f..8e39564ff 100644 Binary files a/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.png and b/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.png differ diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy@2x.png b/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy@2x.png index e70edbd6d..39cc13a42 100644 Binary files a/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy@2x.png and b/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy@2x.png differ diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/solution.md b/1-js/06-advanced-functions/03-closure/8-make-army/solution.md index fb6b4c8c7..a00f3132c 100644 --- a/1-js/06-advanced-functions/03-closure/8-make-army/solution.md +++ b/1-js/06-advanced-functions/03-closure/8-make-army/solution.md @@ -55,9 +55,13 @@ function makeArmy() { Il risultato è che tutte le funzioni `shooter` la prendono dallo stesso lexical envrironment esterno, in cui l'ultimo valore è `i=10`. +<<<<<<< HEAD Questo può essere sistemato molto facilmente: +======= +We can fix it by moving the variable definition into the loop: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb -```js run +```js run demo function makeArmy() { let shooters = []; @@ -81,7 +85,13 @@ army[5](); // 5 ``` Ora funziona correttamente, perché ogni volta che viene eseguito il blocco di codice `for (..) {...}`, viene creato un nuovo Lexical Environment, con il corrispondente valore `i`. +<<<<<<< HEAD Quindi, il valore di `i` ora si trova più "vicino". Non più nel lexical environment di `makeArmy()`, ma in quello del corrispondente ciclo. Uno `shooter` preleva il valore esattamente da dove è stato creato. +======= +Now it works correctly, because every time the code block in `for (let i=0...) {...}` is executed, a new Lexical Environment is created for it, with the corresponding variable `i`. + +So, the value of `i` now lives a little bit closer. Not in `makeArmy()` Lexical Environment, but in the Lexical Environment that corresponds the current loop iteration. That's why now it works. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ![](lexenv-makearmy.png) @@ -89,7 +99,6 @@ Qui abbiamo riscritto `while` in `for`. Eì possibile farlo in un altro modo, vediamolo per capirlo meglio: - ```js run function makeArmy() { let shooters = []; diff --git a/1-js/06-advanced-functions/03-closure/article.md b/1-js/06-advanced-functions/03-closure/article.md index 4d57c0083..ae7613a42 100644 --- a/1-js/06-advanced-functions/03-closure/article.md +++ b/1-js/06-advanced-functions/03-closure/article.md @@ -70,7 +70,11 @@ L'oggetto Lexical Environment consiste di due parti: 1. *Environment Record* -- un oggetto che contiene tutte le variabili locali e le relative proprietà (e alcune altre informazioni come il valore di `this`). 2. Un riferimento al *outer lexical environment* (lexical environment esterno). +<<<<<<< HEAD Quindi, una "variabile" è solamente una proprietà di questo speciale oggetto, l'Environment Record. "Per ottenere o modificare una variabile" si traduce in "per ottenere o modificare una variabile del Lexical Environment". +======= +**So, a "variable" is just a property of the special internal object, Environment Record. "To get or change a variable" means "to get or change a property of that object".** +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Ad esempio, in questo semplice codice, esiste un solo Lexical Environment: @@ -100,7 +104,15 @@ Per ricapitolare: ### Dichiarazione di funzione +<<<<<<< HEAD Le dichiarazioni di funzione sono speciali. A differenza di `let` per le variabili, vengono processate non quando il flusso d'esecuzione le incontra, ma quando il Lexical Environment viene creato. Nel caso del global Lexical Environment, significa all'inizio dello script. +======= +Till now, we only observed variables. Now enter Function Declarations. + +**Unlike `let` variables, they are fully initialized not when the execution reaches them, but earlier, when a Lexical Environment is created.** + +For top-level functions, it means the moment when the script is started. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Questo è il motivo per cui possiamo chiamare una funzione prima della sua definizione. @@ -111,10 +123,18 @@ Il codice sotto dimostra che il Lexical Environment inizialmente non è vuoto. C ### Lexical Environment interno ed esterno +<<<<<<< HEAD Durante la sua esecuzione, `say()` utilizza una variabile esterna, quindi proviamo ad analizzare più in dettaglio cosa sta succedendo. +======= +Now let's go on and explore what happens when a function accesses an outer variable. + +During the call, `say()` uses the outer variable `phrase`, let's look at the details of what's going on. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Inizialmente, quando una funzione viene eseguita, viene creato un nuovo Lexical Environment per la funzione. Questa è una regola generale valida per tutte le funzioni. Questo Lexical Environment viene utilizzato per memorizzare le variabili locali e i parametri. +For instance, for `say("John")`, it looks like this (the execution is at the line, labelled with an arrow): + +<<<<<<< HEAD In questa figura vediamo il contenuto del Lexical Environments quando il flusso d'esecuzione si trova all0interno di `say("John")`, nella riga etichettata con la freccia: +======= +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ![lexical environment](lexical-environment-simple.png) Durante la chiamata di funzione abbiamo due Lexical Environments: quello interno (relativo alla funzione) e quello esterno (globale): +<<<<<<< HEAD - Il Lexical Environment interno che corrisponde all'esecuzione di `say`. Che possiede una sola variabile: `name`, che è l'unico argomento della funzione. Abbiamo invocato `say("John")`, quindi il valore di name `name` è `"John"`. - Il Lexical Environment esterno è quello globale. @@ -140,11 +164,32 @@ Il Lexical Environment interno ha un riferimento a quello esterno, memorizzato s **Quando il codice vuole accedere ad una variabile -- come prima cosa la si cerca nel Lexical Environment interno, successivamente in quello esterno, poi in quello ancora più esterno e cosi via fino a quello più esterno.** Se una variabile non viene trovata, si ha un errore in strict mode. Senza `use strict`, viene creata una variabile globale, per consentire la retro-compatibilità. +======= +So, during the function call we have two Lexical Environments: the inner one (for the function call) and the outer one (global): + +- The inner Lexical Environment corresponds to the current execution of `say`. + + It has a single variable: `name`, the function argument. We called `say("John")`, so the value of `name` is `"John"`. +- The outer Lexical Environment is the global Lexical Environment. + + It has `phrase` and the function itself. + +The inner Lexical Environment has a reference to the outer one. + +**When the code wants to access a variable -- the inner Lexical Environment is searched first, then the outer one, then the more outer one and so on until the end of the chain.** +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Vediamo come procede la ricerca nel nostro esempio: +<<<<<<< HEAD - Quando `alert` all'interno di `say` vuole accedere a `name`, trova la variabile immediatamente nel Lexical Environment della funzione. - Quando vuole accedere a `phrase`, non trova nessun `phrase` localmente, quindi segue il riferimento `outer` e la trova nel Lexical Environment esterno. +======= +Let's see how the search proceeds in our example: + +- When the `alert` inside `say` wants to access `name`, it finds it immediately in the function Lexical Environment. +- When it wants to access `phrase`, then there is no `phrase` locally, so it follows the reference to the enclosing Lexical Environment and finds it there. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ![lexical environment lookup](lexical-environment-simple-lookup.png) @@ -211,11 +256,19 @@ function sayHiBye(firstName, lastName) { } ``` +<<<<<<< HEAD Qui la funzione *annidata* `getFullName()` è stata creata per comodità. Può accedere alle variabili esterne quindi può ritornarne il nome completo. Un'altra cosa interessante, una funzione annidata può essere ritornata: sia come proprietà di un nuovo oggetto(se la funzione esterna crea un oggetto con dei metodi) o come risultato stesso. Può essere salvata e utilizzata da qualsiasi altra parte. Non ha importanza dove, avrà comunque accesso alle stesse variabili esterne. Un esempio con il costruttore (vedere il capitolo ): +======= +Here the *nested* function `getFullName()` is made for convenience. It can access the outer variables and so can return the full name. Nested functions are quite common in JavaScript. + +What's much more interesting, a nested function can be returned: either as a property of a new object (if the outer function creates an object with methods) or as a result by itself. It can then be used somewhere else. No matter where, it still has access to the same outer variables. + +For instance, here the nested function is assigned to the new object by the [constructor function](info:constructor-new): +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run // constructor function returns a new object @@ -228,10 +281,14 @@ function User(name) { } let user = new User("John"); -user.sayHi(); // the method code has access to the outer "name" +user.sayHi(); // the method "sayHi" code has access to the outer "name" ``` +<<<<<<< HEAD Un esempio con ritorno di funzione: +======= +And here we just create and return a "counting" function: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb ```js run function makeCounter() { @@ -249,7 +306,11 @@ alert( counter() ); // 1 alert( counter() ); // 2 ``` +<<<<<<< HEAD Proseguiamo con l'esempio `makeCounter`. Crea una funzione "contatore" che ritorna il numero successivo ad ogni invocazione. Nonostante sia semplice, delle varianti modificate di questo codice hanno usi pratici, ad esempio, come [generatore di numeri pseudocasuali](https://en.wikipedia.org/wiki/Pseudorandom_number_generator), e molto altro. Quindi l'esempio non è cosi artificiale. +======= +Let's go on with the `makeCounter` example. It creates the "counter" function that returns the next number on each invocation. Despite being simple, slightly modified variants of that code have practical uses, for instance, as a [pseudorandom number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator), and more. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Come funziona il counter internamente? @@ -265,8 +326,13 @@ In questo esempio `count` viene trovata nel caso `2`. Quando una variabile ester Ci sono due considerazioni da fare: +<<<<<<< HEAD 1. Possiamo in qualche modo resettare la variabile `counter` da una parte di codice che non appartiene a `makeCounter`? Ad esempio dopo la chiamata ad `alert`. 2. Se chiamiamo `makeCounter()` più volte -- ritorna più funzioni `counter`. Sono indipendenti tra loro oppure condividono il valore `count`? +======= +1. Can we somehow reset the counter `count` from the code that doesn't belong to `makeCounter`? E.g. after `alert` calls in the example above. +2. If we call `makeCounter()` multiple times -- it returns many `counter` functions. Are they independent or do they share the same `count`? +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Prima di continuare provate a rispondere. @@ -276,8 +342,13 @@ Fatto? Okay, allora vediamo le risposte. +<<<<<<< HEAD 1. No non c'è alcun modo di farlo. La variabile `counter` è locale alla funzione, non possiamo accedervi dall'esterno. 2. Per ogni chiamata a `makeCounter()` viene creato un nuovo Lexical Environment, con i suoi `counter`. Quindi le funzioni `counter` sono indipendenti. +======= +1. There is no way: `count` is a local function variable, we can't access it from the outside. +2. For every call to `makeCounter()` a new function Lexical Environment is created, with its own `count`. So the resulting `counter` functions are independent. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Vediamo un esempio di quanto detto: @@ -303,9 +374,17 @@ Dovrebbe esservi abbastanza chiara la situazione delle variabili esterne. In alc ## Environment nel dettaglio +<<<<<<< HEAD Ora che avete una maggiore conoscenza delle closure, possiamo scendere più in profondità. Qui analizziamo cosa succede nell'esempio `makeCounter` passo per passo, seguitelo attentamente per essere certi di comprenderne il funzionamento. Da notare un ulteriore proprietà `[[Environment]]` di cui non abbiamo ancora parlato. +======= +Now that you understand how closures work generally, that's already very good. + +Here's what's going on in the `makeCounter` example step-by-step, follow it to make sure that you know things in the very detail. + +Please note the additional `[[Environment]]` property is covered here. We didn't mention it before for simplicity. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb 1. Quando lo script inizia, si ha solamente il Lexical Environment globale: @@ -313,7 +392,11 @@ Qui analizziamo cosa succede nell'esempio `makeCounter` passo per passo, seguite Inizialmente si ha solamente la funzione `makeCounter`, perché è una dichiarazione di funzione. Non ha ancora eseguito. +<<<<<<< HEAD Tutte le funzioni "alla nascita" ricevono una proprietà nascosta `[[Environment]]` con un riferimento al Lexical Environment corrispondente. Non lo avevamo spiegato finora, ma questo è il meccanismo con cui viene collegata la funzione al rispettivo Lexical Environment. +======= + **All functions "on birth" receive a hidden property `[[Environment]]` with a reference to the Lexical Environment of their creation.** We didn't talk about it yet, but that's how the function knows where it was made. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Qui, `makeCounter` viene creata nel Lexical Environment globale, quindi `[[Environment]]` contiene un riferimento a questo. @@ -389,13 +472,23 @@ Durante un'intervista ad un frontend developer, si potrebbe porre la domanda: "c ## Blocchi di codice e cicli, IIFE +<<<<<<< HEAD Gli esempi sopra si sono concentrati sulle funzioni. Ma i Lexical Environment esistono anche per i blocchi di codice `{...}`. Vengono creati quando un blocco di codice viene eseguito e contiene le variabili locali. Vediamo un paio di esempi. +======= +The examples above concentrated on functions. But a Lexical Environment exists for any code block `{...}`. + +A Lexical Environment is created when a code block runs and contains block-local variables. Here are a couple of examples. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb -## If +### If +<<<<<<< HEAD In questo esempio, quando l'esecuzione arriva dentro il blocco `if`, viene creato un Lexical Environment relativo a "if": +======= +In the example below, the `user` variable exists only in the `if` block: +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb - ```js untrusted run no-strict refresh - var phrase = "Hello"; +For historical reasons, in-browser `window` object is a bit messed up. - function sayHi() { - alert(phrase); - } +1. It provides the "browser window" functionality, besides playing the role of a global object. - // can read from window - alert( window.phrase ); // Hello (global var) - alert( window.sayHi ); // function (global function declaration) + We can use `window` to access properties and methods, specific to the browser window: - // can write to window (creates a new global variable) - window.test = 5; + ```js run + alert(window.innerHeight); // shows the browser window height - alert(test); // 5 + window.open('http://google.com'); // opens a new browser window ``` -...But the global object does not have variables declared with `let/const`! +2. Top-level `var` variables and function declarations automatically become properties of `window`. -```js untrusted run no-strict refresh -*!*let*/!* user = "John"; -alert(user); // John + For instance: + ```js untrusted run no-strict refresh + var x = 5; -alert(window.user); // undefined, don't have let -alert("user" in window); // false -``` + alert(window.x); // 5 (var x becomes a property of window) -```smart header="The global object is not a global Environment Record" -In versions of ECMAScript prior to ES-2015, there were no `let/const` variables, only `var`. And global object was used as a global Environment Record (wordings were a bit different, but that's the gist). + window.x = 0; -But starting from ES-2015, these entities are split apart. There's a global Lexical Environment with its Environment Record. And there's a global object that provides *some* of the global variables. + alert(x); // 0, variable modified + ``` -As a practical difference, global `let/const` variables are definitively properties of the global Environment Record, but they do not exist in the global object. + Please note, that doesn't happen with more modern `let/const` declarations: -Naturally, that's because the idea of a global object as a way to access "all global things" comes from ancient times. Nowadays is not considered to be a good thing. Modern language features like `let/const` do not make friends with it, but old ones are still compatible. -``` + ```js untrusted run no-strict refresh + let x = 5; -## Uses of "window" + alert(window.x); // undefined ("let" doesn't create a window property) + ``` -In server-side environments like Node.JS, the `global` object is used exceptionally rarely. Probably it would be fair to say "never". +3. Also, all scripts share the same global scope, so variables declared in one ` -Usually, it's not a good idea to use it, but here are some examples you can meet. + + ``` -1. To access exactly the global variable if the function has the local one with the same name. +4. And, a minor thing, but still: the value of `this` in the global scope is `window`. ```js untrusted run no-strict refresh - var user = "Global"; - - function sayHi() { - var user = "Local"; - - *!* - alert(window.user); // Global - */!* - } - - sayHi(); + alert(this); // window ``` - Such use is a workaround. Would be better to name variables differently, that won't require use to write the code it this way. And please note `"var"` before `user`. The trick doesn't work with `let` variables. +Why was it made like this? At the time of the language creation, the idea to merge multiple aspects into a single `window` object was to "make things simple". But since then many things changed. Tiny scripts became big applications that require proper architecture. -2. To check if a certain global variable or a builtin exists. +Is it good that different scripts (possibly from different sources) see variables of each other? - For instance, we want to check whether a global function `XMLHttpRequest` exists. +No, it's not, because it may lead to naming conflicts: the same variable name can be used in two scripts for different purposes, so they will conflict with each other. - We can't write `if (XMLHttpRequest)`, because if there's no `XMLHttpRequest`, there will be an error (variable not defined). +As of now, the multi-purpose `window` is considered a design mistake in the language. - But we can read it from `window.XMLHttpRequest`: +Luckily, there's a "road out of hell", called "JavaScript modules". - ```js run - if (window.XMLHttpRequest) { - alert('XMLHttpRequest exists!') - } - ``` +If we set `type="module"` attribute on a ` ``` - This doesn't use `window`, but is (theoretically) less reliable, because `typeof` may use a local XMLHttpRequest, and we want the global one. +- Two modules that do not see variables of each other: + ```html run + -3. To take the variable from the right window. That's probably the most valid use case. + + ``` - A browser may open multiple windows and tabs. A window may also embed another one in ` - - ``` - Here, first two alerts use the current window, and the latter two take variables from `iframe` window. Can be any variables if `iframe` originates from the same protocol/host/port. +**Using ` - - - - diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/index.html b/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/index.html deleted file mode 100644 index fdee13d01..000000000 --- a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Console clock - - - - - - - - - - diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/task.md b/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/task.md deleted file mode 100644 index 71131816b..000000000 --- a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/task.md +++ /dev/null @@ -1,9 +0,0 @@ -importance: 5 - ---- - -# Rewrite to prototypes - -The `Clock` class is written in functional style. Rewrite it using prototypes. - -P.S. The clock ticks in the console, open it to see. diff --git a/1-js/07-object-oriented-programming/08-class-patterns/article.md b/1-js/07-object-oriented-programming/08-class-patterns/article.md deleted file mode 100644 index 92b521c19..000000000 --- a/1-js/07-object-oriented-programming/08-class-patterns/article.md +++ /dev/null @@ -1,240 +0,0 @@ - -# Class patterns - -```quote author="Wikipedia" -In object-oriented programming, a *class* is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions or methods). -``` - -There's a special syntax construct and a keyword `class` in JavaScript. But before studying it, we should consider that the term "class" comes from the theory of object-oriented programming. The definition is cited above, and it's language-independent. - -In JavaScript there are several well-known programming patterns to make classes even without using the `class` keyword. And here we'll talk about them first. - -The `class` construct will be described in the next chapter, but in JavaScript it's a "syntax sugar" and an extension of one of the patterns that we'll study here. - - -## Functional class pattern - -The constructor function below can be considered a "class" according to the definition: - -```js run -function User(name) { - this.sayHi = function() { - alert(name); - }; -} - -let user = new User("John"); -user.sayHi(); // John -``` - -It follows all parts of the definition: - -1. It is a "program-code-template" for creating objects (callable with `new`). -2. It provides initial values for the state (`name` from parameters). -3. It provides methods (`sayHi`). - -This is called *functional class pattern*. - -In the functional class pattern, local variables and nested functions inside `User`, that are not assigned to `this`, are visible from inside, but not accessible by the outer code. - -So we can easily add internal functions and variables, like `calcAge()` here: - -```js run -function User(name, birthday) { -*!* - // only visible from other methods inside User - function calcAge() { - return new Date().getFullYear() - birthday.getFullYear(); - } -*/!* - - this.sayHi = function() { - alert(`${name}, age:${calcAge()}`); - }; -} - -let user = new User("John", new Date(2000, 0, 1)); -user.sayHi(); // John, age:17 -``` - -In this code variables `name`, `birthday` and the function `calcAge()` are internal, *private* to the object. They are only visible from inside of it. - -On the other hand, `sayHi` is the external, *public* method. The external code that creates `user` can access it. - -This way we can hide internal implementation details and helper methods from the outer code. Only what's assigned to `this` becomes visible outside. - -## Factory class pattern - -We can create a class without using `new` at all. - -Like this: - -```js run -function User(name, birthday) { - // only visible from other methods inside User - function calcAge() { - return new Date().getFullYear() - birthday.getFullYear(); - } - - return { - sayHi() { - alert(`${name}, age:${calcAge()}`); - } - }; -} - -*!* -let user = User("John", new Date(2000, 0, 1)); -*/!* -user.sayHi(); // John, age:17 -``` - -As we can see, the function `User` returns an object with public properties and methods. The only benefit of this method is that we can omit `new`: write `let user = User(...)` instead of `let user = new User(...)`. In other aspects it's almost the same as the functional pattern. - -## Prototype-based classes - -Prototype-based classes are the most important and generally the best. Functional and factory class patterns are rarely used in practice. - -Soon you'll see why. - -Here's the same class rewritten using prototypes: - -```js run -function User(name, birthday) { -*!* - this._name = name; - this._birthday = birthday; -*/!* -} - -*!* -User.prototype._calcAge = function() { -*/!* - return new Date().getFullYear() - this._birthday.getFullYear(); -}; - -User.prototype.sayHi = function() { - alert(`${this._name}, age:${this._calcAge()}`); -}; - -let user = new User("John", new Date(2000, 0, 1)); -user.sayHi(); // John, age:17 -``` - -The code structure: - -- The constructor `User` only initializes the current object state. -- Methods are added to `User.prototype`. - -As we can see, methods are lexically not inside `function User`, they do not share a common lexical environment. If we declare variables inside `function User`, then they won't be visible to methods. - -So, there is a widely known agreement that internal properties and methods are prepended with an underscore `"_"`. Like `_name` or `_calcAge()`. Technically, that's just an agreement, the outer code still can access them. But most developers recognize the meaning of `"_"` and try not to touch prefixed properties and methods in the external code. - -Here are the advantages over the functional pattern: - -- In the functional pattern, each object has its own copy of every method. We assign a separate copy of `this.sayHi = function() {...}` and other methods in the constructor. -- In the prototypal pattern, all methods are in `User.prototype` that is shared between all user objects. An object itself only stores the data. - -So the prototypal pattern is more memory-efficient. - -...But not only that. Prototypes allow us to setup the inheritance in a really efficient way. Built-in JavaScript objects all use prototypes. Also there's a special syntax construct: "class" that provides nice-looking syntax for them. And there's more, so let's go on with them. - -## Prototype-based inheritance for classes - -Let's say we have two prototype-based classes. - -`Rabbit`: - -```js -function Rabbit(name) { - this.name = name; -} - -Rabbit.prototype.jump = function() { - alert(`${this.name} jumps!`); -}; - -let rabbit = new Rabbit("My rabbit"); -``` - -![](rabbit-animal-independent-1.png) - -...And `Animal`: - -```js -function Animal(name) { - this.name = name; -} - -Animal.prototype.eat = function() { - alert(`${this.name} eats.`); -}; - -let animal = new Animal("My animal"); -``` - -![](rabbit-animal-independent-2.png) - -Right now they are fully independent. - -But we'd want `Rabbit` to extend `Animal`. In other words, rabbits should be based on animals, have access to methods of `Animal` and extend them with its own methods. - -What does it mean in the language of prototypes? - -Right now methods for `rabbit` objects are in `Rabbit.prototype`. We'd like `rabbit` to use `Animal.prototype` as a "fallback", if the method is not found in `Rabbit.prototype`. - -So the prototype chain should be `rabbit` -> `Rabbit.prototype` -> `Animal.prototype`. - -Like this: - -![](class-inheritance-rabbit-animal.png) - -The code to implement that: - -```js run -// Same Animal as before -function Animal(name) { - this.name = name; -} - -// All animals can eat, right? -Animal.prototype.eat = function() { - alert(`${this.name} eats.`); -}; - -// Same Rabbit as before -function Rabbit(name) { - this.name = name; -} - -Rabbit.prototype.jump = function() { - alert(`${this.name} jumps!`); -}; - -*!* -// setup the inheritance chain -Rabbit.prototype.__proto__ = Animal.prototype; // (*) -*/!* - -let rabbit = new Rabbit("White Rabbit"); -*!* -rabbit.eat(); // rabbits can eat too -*/!* -rabbit.jump(); -``` - -The line `(*)` sets up the prototype chain. So that `rabbit` first searches methods in `Rabbit.prototype`, then `Animal.prototype`. And then, just for completeness, let's mention that if the method is not found in `Animal.prototype`, then the search continues in `Object.prototype`, because `Animal.prototype` is a regular plain object, so it inherits from it. - -So here's the full picture: - -![](class-inheritance-rabbit-animal-2.png) - -## Summary - -The term "class" comes from the object-oriented programming. In JavaScript it usually means the functional class pattern or the prototypal pattern. The prototypal pattern is more powerful and memory-efficient, so it's recommended to stick to it. - -According to the prototypal pattern: -1. Methods are stored in `Class.prototype`. -2. Prototypes inherit from each other. - -In the next chapter we'll study `class` keyword and construct. It allows to write prototypal classes shorter and provides some additional benefits. diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2.png b/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2.png deleted file mode 100644 index ad4a40932..000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2@2x.png b/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2@2x.png deleted file mode 100644 index 199ed3ee6..000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal.png b/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal.png deleted file mode 100644 index 70708c284..000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal@2x.png b/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal@2x.png deleted file mode 100644 index 0db130181..000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png deleted file mode 100644 index e63d7d784..000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png deleted file mode 100644 index 3d1be9cce..000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2.png deleted file mode 100644 index 435ec5f89..000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2@2x.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2@2x.png deleted file mode 100644 index 5731da73a..000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/index.html b/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/index.html deleted file mode 100644 index fdee13d01..000000000 --- a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Console clock - - - - - - - - - - diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/clock.js b/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/clock.js deleted file mode 100644 index d312c93ea..000000000 --- a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/clock.js +++ /dev/null @@ -1,34 +0,0 @@ - - -function Clock({ template }) { - this._template = template; -} - -Clock.prototype._render = function() { - let date = new Date(); - - let hours = date.getHours(); - if (hours < 10) hours = '0' + hours; - - let mins = date.getMinutes(); - if (mins < 10) mins = '0' + mins; - - let secs = date.getSeconds(); - if (secs < 10) secs = '0' + secs; - - let output = this._template - .replace('h', hours) - .replace('m', mins) - .replace('s', secs); - - console.log(output); -}; - -Clock.prototype.stop = function() { - clearInterval(this._timer); -}; - -Clock.prototype.start = function() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); -}; diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/index.html b/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/index.html deleted file mode 100644 index fdee13d01..000000000 --- a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Console clock - - - - - - - - - - diff --git a/1-js/07-object-oriented-programming/09-class/article.md b/1-js/07-object-oriented-programming/09-class/article.md deleted file mode 100644 index 71878ae35..000000000 --- a/1-js/07-object-oriented-programming/09-class/article.md +++ /dev/null @@ -1,355 +0,0 @@ - -# Classes - -The "class" construct allows to define prototype-based classes with a clean, nice-looking syntax. - -## The "class" syntax - -The `class` syntax is versatile, we'll start with a simple example first. - -Here's a prototype-based class `User`: - -```js run -function User(name) { - this.name = name; -} - -User.prototype.sayHi = function() { - alert(this.name); -} - -let user = new User("John"); -user.sayHi(); -``` - -...And that's the same using `class` syntax: - -```js run -class User { - - constructor(name) { - this.name = name; - } - - sayHi() { - alert(this.name); - } - -} - -let user = new User("John"); -user.sayHi(); -``` - -It's easy to see that the two examples are alike. Just please note that methods in a class do not have a comma between them. Novice developers sometimes forget it and put a comma between class methods, and things don't work. That's not a literal object, but a class syntax. - -So, what exactly does `class` do? We may think that it defines a new language-level entity, but that would be wrong. - -The `class User {...}` here actually does two things: - -1. Declares a variable `User` that references the function named `"constructor"`. -2. Puts methods listed in the definition into `User.prototype`. Here, it includes `sayHi` and the `constructor`. - -Here's the code to dig into the class and see that: - -```js run -class User { - constructor(name) { this.name = name; } - sayHi() { alert(this.name); } -} - -*!* -// proof: User is the "constructor" function -*/!* -alert(User === User.prototype.constructor); // true - -*!* -// proof: there are two methods in its "prototype" -*/!* -alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi -``` - -Here's the illustration of what `class User` creates: - -![](class-user.png) - - - -So `class` is a special syntax to define a constructor together with its prototype methods. - -...But not only that. There are minor tweaks here and there: - -Constructors require `new` -: Unlike a regular function, a class `constructor` can't be called without `new`: - -```js run -class User { - constructor() {} -} - -alert(typeof User); // function -User(); // Error: Class constructor User cannot be invoked without 'new' -``` - -Different string output -: If we output it like `alert(User)`, some engines show `"class User..."`, while others show `"function User..."`. - -Please don't be confused: the string representation may vary, but that's still a function, there is no separate "class" entity in JavaScript language. - -Class methods are non-enumerable -: A class definition sets `enumerable` flag to `false` for all methods in the `"prototype"`. That's good, because if we `for..in` over an object, we usually don't want its class methods. - -Classes have a default `constructor() {}` -: If there's no `constructor` in the `class` construct, then an empty function is generated, same as if we had written `constructor() {}`. - -Classes always `use strict` -: All code inside the class construct is automatically in strict mode. - -### Getters/setters - -Classes may also include getters/setters. Here's an example with `user.name` implemented using them: - -```js run -class User { - - constructor(name) { - // invokes the setter - this.name = name; - } - -*!* - get name() { -*/!* - return this._name; - } - -*!* - set name(value) { -*/!* - if (value.length < 4) { - alert("Name is too short."); - return; - } - this._name = value; - } - -} - -let user = new User("John"); -alert(user.name); // John - -user = new User(""); // Name too short. -``` - -Internally, getters and setters are also created on the `User` prototype, like this: - -```js -Object.defineProperties(User.prototype, { - name: { - get() { - return this._name - }, - set(name) { - // ... - } - } -}); -``` - -### Only methods - -Unlike object literals, no `property:value` assignments are allowed inside `class`. There may be only methods and getters/setters. There is some work going on in the specification to lift that limitation, but it's not yet there. - -If we really need to put a non-function value into the prototype, then we can alter `prototype` manually, like this: - -```js run -class User { } - -User.prototype.test = 5; - -alert( new User().test ); // 5 -``` - -So, technically that's possible, but we should know why we're doing it. Such properties will be shared among all objects of the class. - -An "in-class" alternative is to use a getter: - -```js run -class User { - get test() { - return 5; - } -} - -alert( new User().test ); // 5 -``` - -From the external code, the usage is the same. But the getter variant is a bit slower. - -## Class Expression - -Just like functions, classes can be defined inside another expression, passed around, returned etc. - -Here's a class-returning function ("class factory"): - -```js run -function makeClass(phrase) { -*!* - // declare a class and return it - return class { - sayHi() { - alert(phrase); - }; - }; -*/!* -} - -let User = makeClass("Hello"); - -new User().sayHi(); // Hello -``` - -That's quite normal if we recall that `class` is just a special form of a function-with-prototype definition. - -And, like Named Function Expressions, such classes also may have a name, that is visible inside that class only: - -```js run -// "Named Class Expression" (alas, no such term, but that's what's going on) -let User = class *!*MyClass*/!* { - sayHi() { - alert(MyClass); // MyClass is visible only inside the class - } -}; - -new User().sayHi(); // works, shows MyClass definition - -alert(MyClass); // error, MyClass not visible outside of the class -``` - -## Static methods - -We can also assign methods to the class function, not to its `"prototype"`. Such methods are called *static*. - -An example: - -```js run -class User { -*!* - static staticMethod() { -*/!* - alert(this === User); - } -} - -User.staticMethod(); // true -``` - -That actually does the same as assigning it as a function property: - -```js -function User() { } - -User.staticMethod = function() { - alert(this === User); -}; -``` - -The value of `this` inside `User.staticMethod()` is the class constructor `User` itself (the "object before dot" rule). - -Usually, static methods are used to implement functions that belong to the class, but not to any particular object of it. - -For instance, we have `Article` objects and need a function to compare them. The natural choice would be `Article.compare`, like this: - -```js run -class Article { - constructor(title, date) { - this.title = title; - this.date = date; - } - -*!* - static compare(articleA, articleB) { - return articleA.date - articleB.date; - } -*/!* -} - -// usage -let articles = [ - new Article("Mind", new Date(2016, 1, 1)), - new Article("Body", new Date(2016, 0, 1)), - new Article("JavaScript", new Date(2016, 11, 1)) -]; - -*!* -articles.sort(Article.compare); -*/!* - -alert( articles[0].title ); // Body -``` - -Here `Article.compare` stands "over" the articles, as a means to compare them. It's not a method of an article, but rather of the whole class. - -Another example would be a so-called "factory" method. Imagine, we need few ways to create an article: - -1. Create by given parameters (`title`, `date` etc). -2. Create an empty article with today's date. -3. ... - -The first way can be implemented by the constructor. And for the second one we can make a static method of the class. - -Like `Article.createTodays()` here: - -```js run -class Article { - constructor(title, date) { - this.title = title; - this.date = date; - } - -*!* - static createTodays() { - // remember, this = Article - return new this("Today's digest", new Date()); - } -*/!* -} - -let article = Article.createTodays(); - -alert( article.title ); // Todays digest -``` - -Now every time we need to create a today's digest, we can call `Article.createTodays()`. Once again, that's not a method of an article, but a method of the whole class. - -Static methods are also used in database-related classes to search/save/remove entries from the database, like this: - -```js -// assuming Article is a special class for managing articles -// static method to remove the article: -Article.remove({id: 12345}); -``` - -## Summary - -The basic class syntax looks like this: - -```js -class MyClass { - constructor(...) { - // ... - } - method1(...) {} - method2(...) {} - get something(...) {} - set something(...) {} - static staticMethod(..) {} - // ... -} -``` - -The value of `MyClass` is a function provided as `constructor`. If there's no `constructor`, then an empty function. - -In any case, methods listed in the class declaration become members of its `prototype`, with the exception of static methods that are written into the function itself and callable as `MyClass.staticMethod()`. Static methods are used when we need a function bound to a class, but not to any object of that class. - -In the next chapter we'll learn more about classes, including inheritance. diff --git a/1-js/07-object-oriented-programming/09-class/class-user.png b/1-js/07-object-oriented-programming/09-class/class-user.png deleted file mode 100644 index 5579e6bbd..000000000 Binary files a/1-js/07-object-oriented-programming/09-class/class-user.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/09-class/class-user@2x.png b/1-js/07-object-oriented-programming/09-class/class-user@2x.png deleted file mode 100644 index 5a85e6589..000000000 Binary files a/1-js/07-object-oriented-programming/09-class/class-user@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/index.html b/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/index.html deleted file mode 100644 index 7ac1db714..000000000 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - Console clock - - - - - - - - - - - diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/index.html b/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/index.html deleted file mode 100644 index b48a2a007..000000000 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - Console clock - - - - - - - - - - - diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object.png b/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object.png deleted file mode 100644 index d4ff37e56..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png deleted file mode 100644 index a54a9d2f8..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-extends.png b/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-extends.png deleted file mode 100644 index 2db88f366..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-extends.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-extends@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-extends@2x.png deleted file mode 100644 index 9539fe9ec..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-extends@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static.png b/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static.png deleted file mode 100644 index 998c82330..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static@2x.png deleted file mode 100644 index 98a80d38f..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object.png deleted file mode 100644 index c5d712632..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object@2x.png deleted file mode 100644 index edc4e841e..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal.png deleted file mode 100644 index 70708c284..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal@2x.png deleted file mode 100644 index 0db130181..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance.png b/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance.png deleted file mode 100644 index 542a0c9fa..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance@2x.png deleted file mode 100644 index 21485062a..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop.png b/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop.png deleted file mode 100644 index 637d17939..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop@2x.png deleted file mode 100644 index af7b443bc..000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/11-instanceof/instanceof.png b/1-js/07-object-oriented-programming/11-instanceof/instanceof.png deleted file mode 100644 index 85aa9a55f..000000000 Binary files a/1-js/07-object-oriented-programming/11-instanceof/instanceof.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/11-instanceof/instanceof@2x.png b/1-js/07-object-oriented-programming/11-instanceof/instanceof@2x.png deleted file mode 100644 index fba771220..000000000 Binary files a/1-js/07-object-oriented-programming/11-instanceof/instanceof@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance.png b/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance.png deleted file mode 100644 index 7cc655036..000000000 Binary files a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance@2x.png b/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance@2x.png deleted file mode 100644 index f53ecf68f..000000000 Binary files a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/index.md b/1-js/07-object-oriented-programming/index.md deleted file mode 100644 index 7053ada81..000000000 --- a/1-js/07-object-oriented-programming/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Objects, classes, inheritance - -In this section we return to objects and learn them even more in-depth. diff --git a/1-js/07-object-oriented-programming/01-property-descriptors/article.md b/1-js/07-object-properties/01-property-descriptors/article.md similarity index 94% rename from 1-js/07-object-oriented-programming/01-property-descriptors/article.md rename to 1-js/07-object-properties/01-property-descriptors/article.md index 14d91800f..7768b3557 100644 --- a/1-js/07-object-oriented-programming/01-property-descriptors/article.md +++ b/1-js/07-object-properties/01-property-descriptors/article.md @@ -3,7 +3,9 @@ As we know, objects can store properties. -Till now, a property was a simple "key-value" pair to us. But an object property is actually a more complex and tunable thing. +Till now, a property was a simple "key-value" pair to us. But an object property is actually a more flexible and powerful thing. + +In this chapter we'll study additional configuration options, and in the next we'll see how to invisibly turn them into getter/setter functions. ## Property flags @@ -296,14 +298,13 @@ Property descriptors work at the level of individual properties. There are also methods that limit access to the *whole* object: [Object.preventExtensions(obj)](mdn:js/Object/preventExtensions) -: Forbids to add properties to the object. +: Forbids the addition of new properties to the object. [Object.seal(obj)](mdn:js/Object/seal) -: Forbids to add/remove properties, sets for all existing properties `configurable: false`. +: Forbids adding/removing of properties. Sets `configurable: false` for all existing properties. [Object.freeze(obj)](mdn:js/Object/freeze) -: Forbids to add/remove/change properties, sets for all existing properties `configurable: false, writable: false`. - +: Forbids adding/removing/changing of properties. Sets `configurable: false, writable: false` for all existing properties. And also there are tests for them: [Object.isExtensible(obj)](mdn:js/Object/isExtensible) diff --git a/1-js/07-object-oriented-programming/02-property-accessors/article.md b/1-js/07-object-properties/02-property-accessors/article.md similarity index 94% rename from 1-js/07-object-oriented-programming/02-property-accessors/article.md rename to 1-js/07-object-properties/02-property-accessors/article.md index fa4d2216d..43cd5ae6d 100644 --- a/1-js/07-object-oriented-programming/02-property-accessors/article.md +++ b/1-js/07-object-properties/02-property-accessors/article.md @@ -85,11 +85,12 @@ alert(user.surname); // Cooper Now we have a "virtual" property. It is readable and writable, but in fact does not exist. ```smart header="Accessor properties are only accessible with get/set" -A property can either be a "data property" or an "accessor property", but not both. +Once a property is defined with `get prop()` or `set prop()`, it's an accessor property, not a data properety any more. -Once a property is defined with `get prop()` or `set prop()`, it's an accessor property. So there must be a getter to read it, and must be a setter if we want to assign it. +- If there's a getter -- we can read `object.prop`, othrewise we can't. +- If there's a setter -- we can set `object.prop=...`, othrewise we can't. -Sometimes it's normal that there's only a setter or only a getter. But the property won't be readable or writable in that case. +And in either case we can't `delete` an accessor property. ``` diff --git a/1-js/07-object-properties/index.md b/1-js/07-object-properties/index.md new file mode 100644 index 000000000..67fcccaff --- /dev/null +++ b/1-js/07-object-properties/index.md @@ -0,0 +1,3 @@ +# Object properties configuration + +In this section we return to objects and study their properties even more in-depth. diff --git a/1-js/08-error-handling/1-try-catch/try-catch-flow.png b/1-js/08-error-handling/1-try-catch/try-catch-flow.png deleted file mode 100644 index 6a91b6329..000000000 Binary files a/1-js/08-error-handling/1-try-catch/try-catch-flow.png and /dev/null differ diff --git a/1-js/08-error-handling/1-try-catch/try-catch-flow@2x.png b/1-js/08-error-handling/1-try-catch/try-catch-flow@2x.png deleted file mode 100644 index 8bf9680fd..000000000 Binary files a/1-js/08-error-handling/1-try-catch/try-catch-flow@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/solution.md b/1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/task.md b/1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/solution.md b/1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/task.md b/1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/solution.md b/1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/task.md b/1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/solution.md b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/task.md b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/article.md b/1-js/08-prototypes/01-prototype-inheritance/article.md similarity index 80% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/article.md rename to 1-js/08-prototypes/01-prototype-inheritance/article.md index fa61cbc89..1641de236 100644 --- a/1-js/07-object-oriented-programming/03-prototype-inheritance/article.md +++ b/1-js/08-prototypes/01-prototype-inheritance/article.md @@ -31,7 +31,13 @@ rabbit.__proto__ = animal; */!* ``` -Please note that `__proto__` is *not the same* as `[[Prototype]]`. That's a getter/setter for it. We'll talk about other ways of setting it later, but for now `__proto__` will do just fine. +```smart header="`__proto__` is a historical getter/setter for `[[Prototype]]`" +Please note that `__proto__` is *not the same* as `[[Prototype]]`. That's a getter/setter for it. + +It exists for historical reasons, in modern language it is replaced with functions `Object.getPrototypeOf/Object.setPrototypeOf` that also get/set the prototype. We'll study the reasons for that and these functions later. + +By the specification, `__proto__` must only be supported by browsers, but in fact all environments including server-side support it. For now, as `__proto__` notation is a little bit more intuitively obvious, we'll use it in the examples. +``` If we look for a property in `rabbit`, and it's missing, JavaScript automatically takes it from `animal`. @@ -62,7 +68,7 @@ Then, when `alert` tries to read property `rabbit.eats` `(**)`, it's not in `rab ![](proto-animal-rabbit.png) -Here we can say that "`animal` is the prototype of `rabbit`" or "`rabbit` prototypally inherits from `animal`". +Here we can say that "`animal` is the prototype of `rabbit`" or "`rabbit` prototypically inherits from `animal`". So if `animal` has a lot of useful properties and methods, then they become automatically available in `rabbit`. Such properties are called "inherited". @@ -106,13 +112,17 @@ let animal = { let rabbit = { jumps: true, +*!* __proto__: animal +*/!* }; let longEar = { earLength: 10, +*!* __proto__: rabbit -} +*/!* +}; // walk is taken from the prototype chain longEar.walk(); // Animal walk @@ -124,15 +134,15 @@ alert(longEar.jumps); // true (from rabbit) There are actually only two limitations: 1. The references can't go in circles. JavaScript will throw an error if we try to assign `__proto__` in a circle. -2. The value of `__proto__` can be either an object or `null`. All other values (like primitives) are ignored. +2. The value of `__proto__` can be either an object or `null`, other types (like primitives) are ignored. Also it may be obvious, but still: there can be only one `[[Prototype]]`. An object may not inherit from two others. -## Read/write rules +## Writing doesn't use prototype The prototype is only used for reading properties. -For data properties (not getters/setters) write/delete operations work directly with the object. +Write/delete operations work directly with the object. In the example below, we assign its own `walk` method to `rabbit`: @@ -146,7 +156,7 @@ let animal = { let rabbit = { __proto__: animal -} +}; *!* rabbit.walk = function() { @@ -161,9 +171,9 @@ From now on, `rabbit.walk()` call finds the method immediately in the object and ![](proto-animal-rabbit-walk-2.png) -For getters/setters -- if we read/write a property, they are looked up in the prototype and invoked. +That's for data properties only, not for accessors. If a property is a getter/setter, then it behaves like a function: getters/setters are looked up in the prototype. -For instance, check out `admin.fullName` property in the code below: +For that reason `admin.fullName` works correctly in the code below: ```js run let user = { @@ -194,15 +204,15 @@ Here in the line `(*)` the property `admin.fullName` has a getter in the prototy ## The value of "this" -An interesting question may arise in the example above: what's the value of `this` inside `set fullName(value)`? Where the properties `this.name` and `this.surname` are written: `user` or `admin`? +An interesting question may arise in the example above: what's the value of `this` inside `set fullName(value)`? Where the properties `this.name` and `this.surname` are written: into `user` or `admin`? The answer is simple: `this` is not affected by prototypes at all. **No matter where the method is found: in an object or its prototype. In a method call, `this` is always the object before the dot.** -So, the setter actually uses `admin` as `this`, not `user`. +So, the setter call `admin.fullName=` uses `admin` as `this`, not `user`. -That is actually a super-important thing, because we may have a big object with many methods and inherit from it. Then we can run its methods on inherited objects and they will modify the state of these objects, not the big one. +That is actually a super-important thing, because we may have a big object with many methods and inherit from it. Then inherited objects can run its methods, and they will modify the state of these objects, not the big one. For instance, here `animal` represents a "method storage", and `rabbit` makes use of it. @@ -244,7 +254,7 @@ As a result, methods are shared, but the object state is not. ## Summary - In JavaScript, all objects have a hidden `[[Prototype]]` property that's either another object or `null`. -- We can use `obj.__proto__` to access it (there are other ways too, to be covered soon). +- We can use `obj.__proto__` to access it (a historical getter/setter, there are other ways, to be covered soon). - The object referenced by `[[Prototype]]` is called a "prototype". - If we want to read a property of `obj` or call a method, and it doesn't exist, then JavaScript tries to find it in the prototype. Write/delete operations work directly on the object, they don't use the prototype (unless the property is actually a setter). - If we call `obj.method()`, and the `method` is taken from the prototype, `this` still references `obj`. So methods always work with the current object even if they are inherited. diff --git a/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.png b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.png new file mode 100644 index 000000000..3ecff3fdd Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty@2x.png b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty@2x.png new file mode 100644 index 000000000..f80e61e68 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty@2x.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.png new file mode 100644 index 000000000..48e02ad18 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain@2x.png new file mode 100644 index 000000000..1a04db885 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain@2x.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.png new file mode 100644 index 000000000..22b867fc8 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2@2x.png new file mode 100644 index 000000000..8d9f9f1ae Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2@2x.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.png new file mode 100644 index 000000000..bc76d64ba Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3@2x.png new file mode 100644 index 000000000..725a4c150 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3@2x.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.png new file mode 100644 index 000000000..29fc6d503 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk@2x.png new file mode 100644 index 000000000..724d6111b Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk@2x.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.png new file mode 100644 index 000000000..d50327f0e Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit@2x.png new file mode 100644 index 000000000..0506b92b6 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit@2x.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.png b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.png new file mode 100644 index 000000000..c37d00780 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin@2x.png new file mode 100644 index 000000000..8c3f546eb Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin@2x.png differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/solution.md b/1-js/08-prototypes/02-function-prototype/1-changing-prototype/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/solution.md rename to 1-js/08-prototypes/02-function-prototype/1-changing-prototype/solution.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/task.md b/1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/task.md rename to 1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/solution.md b/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/solution.md rename to 1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/task.md b/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/task.md rename to 1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/task.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/article.md b/1-js/08-prototypes/02-function-prototype/article.md similarity index 76% rename from 1-js/07-object-oriented-programming/04-function-prototype/article.md rename to 1-js/08-prototypes/02-function-prototype/article.md index d4b453850..669a9a658 100644 --- a/1-js/07-object-oriented-programming/04-function-prototype/article.md +++ b/1-js/08-prototypes/02-function-prototype/article.md @@ -1,18 +1,14 @@ # F.prototype -In modern JavaScript we can set a prototype using `__proto__`, as described in the previous article. But it wasn't like that all the time. +Remember, new objects can be created with a constructor function, like `new F()`. -JavaScript has had prototypal inheritance from the beginning. It was one of the core features of the language. +If `F.prototype` is an object, then `new` operator uses it to set `[[Prototype]]` for the new object. -But in the old times, there was another (and the only) way to set it: to use a `"prototype"` property of the constructor function. And there are still many scripts that use it. +```smart +JavaScript had prototypal inheritance from the beginning. It was one of the core features of the language. -## The "prototype" property - -As we know already, `new F()` creates a new object. - -When a new object is created with `new F()`, the object's `[[Prototype]]` is set to `F.prototype`. - -In other words, if `F` has a `prototype` property with a value of the object type, then `new` operator uses it to set `[[Prototype]]` for the new object. +But in the old times, there was no direct access to it. The only thing that worked reliably was a `"prototype"` property of the constructor function, described in this chapter. So there are many scripts that still use it. +``` Please note that `F.prototype` here means a regular property named `"prototype"` on `F`. It sounds something similar to the term "prototype", but here we really mean a regular property with this name. @@ -42,8 +38,13 @@ That's the resulting picture: ![](proto-constructor-animal-rabbit.png) -On the picture, `"prototype"` is a horizontal arrow, it's a regular property, and `[[Prototype]]` is vertical, meaning the inheritance of `rabbit` from `animal`. +On the picture, `"prototype"` is a horizontal arrow, meaning a regular property, and `[[Prototype]]` is vertical, meaning the inheritance of `rabbit` from `animal`. + +```smart header="`F.prototype` only used at `new F` time" +`F.prototype` property is only used when `new F` is called, it assigns `[[Prototype]]` of the new object. After that, there's no connection between `F.prototype` and the new object. Think of it as a "one-time gift". +If, after the creation, `F.prototype` property changes (`F.property = `), then new objects created by `new F` will have another object as `[[Prototype]]`, but already existing objects keep the old one. +``` ## Default F.prototype, constructor property @@ -139,7 +140,7 @@ Rabbit.prototype.jumps = true // the default Rabbit.prototype.constructor is preserved ``` -Or, alternatively, recreate the `constructor` property it manually: +Or, alternatively, recreate the `constructor` property manually: ```js Rabbit.prototype = { diff --git a/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.png b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.png new file mode 100644 index 000000000..6ab9c6934 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.png differ diff --git a/1-js/08-prototypes/02-function-prototype/function-prototype-constructor@2x.png b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor@2x.png new file mode 100644 index 000000000..3beb0a573 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring.png b/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring.png new file mode 100644 index 000000000..c47938674 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring.png differ diff --git a/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring@2x.png b/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring@2x.png new file mode 100644 index 000000000..293c88bf9 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/native-prototypes-classes.png b/1-js/08-prototypes/02-function-prototype/native-prototypes-classes.png new file mode 100644 index 000000000..c4658a929 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/native-prototypes-classes.png differ diff --git a/1-js/08-prototypes/02-function-prototype/native-prototypes-classes@2x.png b/1-js/08-prototypes/02-function-prototype/native-prototypes-classes@2x.png new file mode 100644 index 000000000..79445fac9 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/native-prototypes-classes@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/object-prototype-1.png b/1-js/08-prototypes/02-function-prototype/object-prototype-1.png new file mode 100644 index 000000000..a69cad4a1 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/object-prototype-1.png differ diff --git a/1-js/08-prototypes/02-function-prototype/object-prototype-1@2x.png b/1-js/08-prototypes/02-function-prototype/object-prototype-1@2x.png new file mode 100644 index 000000000..9d661ac17 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/object-prototype-1@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/object-prototype.png b/1-js/08-prototypes/02-function-prototype/object-prototype.png new file mode 100644 index 000000000..820ffcc73 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/object-prototype.png differ diff --git a/1-js/08-prototypes/02-function-prototype/object-prototype@2x.png b/1-js/08-prototypes/02-function-prototype/object-prototype@2x.png new file mode 100644 index 000000000..1eb7e8749 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/object-prototype@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.png b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.png new file mode 100644 index 000000000..4b381460a Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.png differ diff --git a/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit@2x.png b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit@2x.png new file mode 100644 index 000000000..c6e6ab5f6 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/rabbit-animal-object.png b/1-js/08-prototypes/02-function-prototype/rabbit-animal-object.png new file mode 100644 index 000000000..3924233dd Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/rabbit-animal-object.png differ diff --git a/1-js/08-prototypes/02-function-prototype/rabbit-animal-object@2x.png b/1-js/08-prototypes/02-function-prototype/rabbit-animal-object@2x.png new file mode 100644 index 000000000..5350a1935 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/rabbit-animal-object@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.png b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.png new file mode 100644 index 000000000..8f3519a74 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.png differ diff --git a/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor@2x.png b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor@2x.png new file mode 100644 index 000000000..07acb8f4a Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor@2x.png differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/solution.md b/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/solution.md rename to 1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/task.md b/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/task.md rename to 1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/solution.md b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/solution.md rename to 1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/task.md b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/task.md rename to 1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/article.md b/1-js/08-prototypes/03-native-prototypes/article.md similarity index 61% rename from 1-js/07-object-oriented-programming/05-native-prototypes/article.md rename to 1-js/08-prototypes/03-native-prototypes/article.md index 4777f356f..d540be5f2 100644 --- a/1-js/07-object-oriented-programming/05-native-prototypes/article.md +++ b/1-js/08-prototypes/03-native-prototypes/article.md @@ -15,17 +15,17 @@ alert( obj ); // "[object Object]" ? Where's the code that generates the string `"[object Object]"`? That's a built-in `toString` method, but where is it? The `obj` is empty! -...But the short notation `obj = {}` is the same as `obj = new Object()`, where `Object` -- is a built-in object constructor function. And that function has `Object.prototype` that references a huge object with `toString` and other functions. +...But the short notation `obj = {}` is the same as `obj = new Object()`, where `Object` is a built-in object constructor function, with its own `prototype` referencing a huge object with `toString` and other methods. -Like this (all that is built-in): +Here's what's going on: ![](object-prototype.png) -When `new Object()` is called (or a literal object `{...}` is created), the `[[Prototype]]` of it is set to `Object.prototype` by the rule that we've discussed in the previous chapter: +When `new Object()` is called (or a literal object `{...}` is created), the `[[Prototype]]` of it is set to `Object.prototype` according to the rule that we discussed in the previous chapter: ![](object-prototype-1.png) -Afterwards when `obj.toString()` is called -- the method is taken from `Object.prototype`. +So then when `obj.toString()` is called the method is taken from `Object.prototype`. We can check it like this: @@ -48,7 +48,7 @@ Other built-in objects such as `Array`, `Date`, `Function` and others also keep For instance, when we create an array `[1, 2, 3]`, the default `new Array()` constructor is used internally. So the array data is written into the new object, and `Array.prototype` becomes its prototype and provides methods. That's very memory-efficient. -By specification, all built-in prototypes have `Object.prototype` on the top. Sometimes people say that "everything inherits from objects". +By specification, all of the built-in prototypes have `Object.prototype` on the top. Sometimes people say that "everything inherits from objects". Here's the overall picture (for 3 built-ins to fit): @@ -82,11 +82,11 @@ As we've seen before, `Object.prototype` has `toString` as well, but `Array.prot ![](native-prototypes-array-tostring.png) -In-browser tools like Chrome developer console also show inheritance (may need to use `console.dir` for built-in objects): +In-browser tools like Chrome developer console also show inheritance (`console.dir` may need to be used for built-in objects): ![](console_dir_array.png) -Other built-in objects also work the same way. Even functions. They are objects of a built-in `Function` constructor, and their methods: `call/apply` and others are taken from `Function.prototype`. Functions have their own `toString` too. +Other built-in objects also work the same way. Even functions -- they are objects of a built-in `Function` constructor, and their methods (`call`/`apply` and others) are taken from `Function.prototype`. Functions have their own `toString` too. ```js run function f() {} @@ -119,11 +119,19 @@ String.prototype.show = function() { "BOOM!".show(); // BOOM! ``` -During the process of development we may have ideas which new built-in methods we'd like to have. And there may be a slight temptation to add them to native prototypes. But that is generally a bad idea. +During the process of development, we may have ideas for new built-in methods we'd like to have, and we may be tempted to add them to native prototypes. But that is generally a bad idea. -Prototypes are global, so it's easy to get a conflict. If two libraries add a method `String.prototype.show`, then one of them overwrites the other one. +```warn +Prototypes are global, so it's easy to get a conflict. If two libraries add a method `String.prototype.show`, then one of them will be overwriting the other. -In modern programming, there is only one case when modifying native prototypes is approved. That's polyfills. In other words, if there's a method in JavaScript specification that is not yet supported by our JavaScript engine (or any of those that we want to support), then may implement it manually and populate the built-in prototype with it. +So, generally, modifying a native prototype is considered a bad idea. +``` + +**In modern programming, there is only one case where modifying native prototypes is approved. That's polyfilling.** + +Polyfilling is a term for making a substitute for a method that exists in JavaScript specification, but not yet supported by current JavaScript engine. + +Then we may implement it manually and populate the built-in prototype with it. For instance: @@ -134,9 +142,9 @@ if (!String.prototype.repeat) { // if there's no such method String.prototype.repeat = function(n) { // repeat the string n times - // actually, the code should be more complex than that, - // throw errors for negative values of "n" - // the full algorithm is in the specification + // actually, the code should be a little bit more complex than that + // (the full algorithm is in the specification) + // but even an imperfect polyfill is often considered good enough return new Array(n + 1).join(this); }; } @@ -144,37 +152,45 @@ if (!String.prototype.repeat) { // if there's no such method alert( "La".repeat(3) ); // LaLaLa ``` + ## Borrowing from prototypes -In the chapter we talked about method borrowing: +In the chapter we talked about method borrowing. + +That's when we take a method from one object and copy it into another. + +Some methods of native prototypes are often borrowed. + +For instance, if we're making an array-like object, we may want to copy some array methods to it. + +E.g. ```js run -function showArgs() { +let obj = { + 0: "Hello", + 1: "world!", + length: 2, +}; + *!* - // borrow join from array and call in the context of arguments - alert( [].join.call(arguments, " - ") ); +obj.join = Array.prototype.join; */!* -} -showArgs("John", "Pete", "Alice"); // John - Pete - Alice +alert( obj.join(',') ); // Hello,world! ``` -Because `join` resides in `Array.prototype`, we can call it from there directly and rewrite it as: +It works, because the internal algorithm of the built-in `join` method only cares about the correct indexes and the `length` property, it doesn't check that the object is indeed the array. And many built-in methods are like that. -```js -function showArgs() { -*!* - alert( Array.prototype.join.call(arguments, " - ") ); -*/!* -} -``` +Another possibility is to inherit by setting `obj.__proto__` to `Array.prototype`, so all `Array` methods are automatically available in `obj`. + +But that's impossible if `obj` already inherits from another object. Remember, we only can inherit from one object at a time. -That's more efficient, because it avoids the creation of an extra array object `[]`. On the other hand, it is longer to write. +Borrowing methods is flexible, it allows to mix functionality from different objects if needed. ## Summary - All built-in objects follow the same pattern: - The methods are stored in the prototype (`Array.prototype`, `Object.prototype`, `Date.prototype` etc). - The object itself stores only the data (array items, object properties, the date). -- Primitives also store methods in prototypes of wrapper objects: `Number.prototype`, `String.prototype`, `Boolean.prototype`. There are no wrapper objects only for `undefined` and `null`. +- Primitives also store methods in prototypes of wrapper objects: `Number.prototype`, `String.prototype`, `Boolean.prototype`. Only `undefined` and `null` do not have wrapper objects. - Built-in prototypes can be modified or populated with new methods. But it's not recommended to change them. Probably the only allowable cause is when we add-in a new standard, but not yet supported by the engine JavaScript method. diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/console_dir_array.png b/1-js/08-prototypes/03-native-prototypes/console_dir_array.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/console_dir_array.png rename to 1-js/08-prototypes/03-native-prototypes/console_dir_array.png diff --git a/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.png b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.png new file mode 100644 index 000000000..6ab9c6934 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor@2x.png b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor@2x.png new file mode 100644 index 000000000..3beb0a573 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.png b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.png new file mode 100644 index 000000000..c47938674 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring@2x.png b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring@2x.png new file mode 100644 index 000000000..293c88bf9 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.png b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.png new file mode 100644 index 000000000..c4658a929 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes@2x.png b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes@2x.png new file mode 100644 index 000000000..79445fac9 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype-1.png b/1-js/08-prototypes/03-native-prototypes/object-prototype-1.png new file mode 100644 index 000000000..a69cad4a1 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/object-prototype-1.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype-1@2x.png b/1-js/08-prototypes/03-native-prototypes/object-prototype-1@2x.png new file mode 100644 index 000000000..9d661ac17 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/object-prototype-1@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype-null.png b/1-js/08-prototypes/03-native-prototypes/object-prototype-null.png new file mode 100644 index 000000000..792c4f423 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/object-prototype-null.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype-null@2x.png b/1-js/08-prototypes/03-native-prototypes/object-prototype-null@2x.png new file mode 100644 index 000000000..d97efa884 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/object-prototype-null@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype.png b/1-js/08-prototypes/03-native-prototypes/object-prototype.png new file mode 100644 index 000000000..820ffcc73 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/object-prototype.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype@2x.png b/1-js/08-prototypes/03-native-prototypes/object-prototype@2x.png new file mode 100644 index 000000000..1eb7e8749 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/object-prototype@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.png b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.png new file mode 100644 index 000000000..4b381460a Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit@2x.png b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit@2x.png new file mode 100644 index 000000000..c6e6ab5f6 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.png b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.png new file mode 100644 index 000000000..8f3519a74 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor@2x.png b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor@2x.png new file mode 100644 index 000000000..07acb8f4a Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor@2x.png differ diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/solution.md b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md similarity index 79% rename from 1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/solution.md rename to 1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md index debaecd6a..a92e17900 100644 --- a/1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/solution.md +++ b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md @@ -1,7 +1,7 @@ The method can take all enumerable keys using `Object.keys` and output their list. -To make `toString` non-enumerable, let's define it using a property descriptor. The syntax of `Object.create` allows to provide an object with property descriptors as the second argument. +To make `toString` non-enumerable, let's define it using a property descriptor. The syntax of `Object.create` allows us to provide an object with property descriptors as the second argument. ```js run *!* @@ -27,3 +27,5 @@ alert(dictionary); // "apple,__proto__" ``` When we create a property using a descriptor, its flags are `false` by default. So in the code above, `dictionary.toString` is non-enumerable. + +See the the chapter [](info:property-descriptors) for review. diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/task.md b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/task.md rename to 1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/solution.md b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/solution.md rename to 1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/task.md b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md similarity index 92% rename from 1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/task.md rename to 1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md index 92653bd8c..09bb7f1ed 100644 --- a/1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/task.md +++ b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md @@ -2,7 +2,7 @@ importance: 5 --- -# The difference beteeen calls +# The difference between calls Let's create a new `rabbit` object: diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/article.md b/1-js/08-prototypes/04-prototype-methods/article.md similarity index 63% rename from 1-js/07-object-oriented-programming/06-prototype-methods/article.md rename to 1-js/08-prototypes/04-prototype-methods/article.md index e253e8af7..5a9838d1c 100644 --- a/1-js/07-object-oriented-programming/06-prototype-methods/article.md +++ b/1-js/08-prototypes/04-prototype-methods/article.md @@ -1,14 +1,18 @@ -# Methods for prototypes +# Prototype methods, objects without __proto__ -In this chapter we cover additional methods to work with a prototype. +In the first chapter of this section, we mentioned that there are modern methods to setup a prototype. -There are also other ways to get/set a prototype, besides those that we already know: +The `__proto__` is considered outdated and somewhat deprecated (in browser-only part of the JavaScript standard). + +The modern methods are: - [Object.create(proto[, descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` and optional property descriptors. - [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- returns the `[[Prototype]]` of `obj`. - [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- sets the `[[Prototype]]` of `obj` to `proto`. +These should be used instead of `__proto__`. + For instance: ```js run @@ -72,7 +76,13 @@ That's for historical reasons. As of now we have all these ways at our disposal. -Technically, we can get/set `[[Prototype]]` at any time. But usually we only set it once at the object creation time, and then do not modify: `rabbit` inherits from `animal`, and that is not going to change. And JavaScript engines are highly optimized to that. Changing a prototype "on-the-fly" with `Object.setPrototypeOf` or `obj.__proto__=` is a very slow operation. But it is possible. +Why was `__proto__` replaced by the functions? That's an interesting question, requiring us to understand why `__proto__` is bad. Read on to get the answer. + +```warn header="Don't reset `[[Prototype]]` unless the speed doesn't matter" +Technically, we can get/set `[[Prototype]]` at any time. But usually we only set it once at the object creation time, and then do not modify: `rabbit` inherits from `animal`, and that is not going to change. + +And JavaScript engines are highly optimized to that. Changing a prototype "on-the-fly" with `Object.setPrototypeOf` or `obj.__proto__=` is a very slow operation, it breaks internal optimizations for object property access operations. So evade it unless you know what you're doing, or JavaScript speed totally doesn't matter for you. +``` ## "Very plain" objects @@ -95,11 +105,13 @@ Here if the user types in `__proto__`, the assignment is ignored! That shouldn't surprise us. The `__proto__` property is special: it must be either an object or `null`, a string can not become a prototype. -But we did not intend to implement such behavior, right? We want to store key/value pairs, and the key named `"__proto__"` was not properly saved. So that's a bug. Here the consequences are not terrible. But in other cases the prototype may indeed be changed, so the execution may go wrong in totally unexpected ways. +But we didn't *intend* to implement such behavior, right? We want to store key/value pairs, and the key named `"__proto__"` was not properly saved. So that's a bug! + +Here the consequences are not terrible. But in other cases the prototype may indeed be changed, so the execution may go wrong in totally unexpected ways. What's worst -- usually developers do not think about such possibility at all. That makes such bugs hard to notice and even turn them into vulnerabilities, especially when JavaScript is used on server-side. -Such thing happens only with `__proto__`. All other properties are "assignable" normally. +Unexpected things also may happen when accessing `toString` property -- that's a function by default, and other built-in properties. How to evade the problem? @@ -111,9 +123,9 @@ The `__proto__` is not a property of an object, but an accessor property of `Obj ![](object-prototype-2.png) -So, if `obj.__proto__` is read or assigned, the corresponding getter/setter is called from its prototype, and it gets/sets `[[Prototype]]`. +So, if `obj.__proto__` is read or set, the corresponding getter/setter is called from its prototype, and it gets/sets `[[Prototype]]`. -As it was said in the beginning: `__proto__` is a way to access `[[Prototype]]`, it is not `[[Prototype]]` itself. +As it was said in the beginning of this tutorial section: `__proto__` is a way to access `[[Prototype]]`, it is not `[[Prototype]]` itself. Now, if we want to use an object as an associative array, we can do it with a little trick: @@ -153,93 +165,31 @@ Please note that most object-related methods are `Object.something(...)`, like ` ```js run let chineseDictionary = Object.create(null); -chineseDictionary.hello = "ni hao"; -chineseDictionary.bye = "zai jian"; +chineseDictionary.hello = "你好"; +chineseDictionary.bye = "再见"; alert(Object.keys(chineseDictionary)); // hello,bye ``` -## Getting all properties - -There are many ways to get keys/values from an object. - -We already know these ones: - -- [Object.keys(obj)](mdn:js/Object/keys) / [Object.values(obj)](mdn:js/Object/values) / [Object.entries(obj)](mdn:js/Object/entries) -- returns an array of enumerable own string property names/values/key-value pairs. These methods only list *enumerable* properties, and those that have *strings as keys*. - -If we want symbolic properties: - -- [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) -- returns an array of all own symbolic property names. - -If we want non-enumerable properties: - -- [Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) -- returns an array of all own string property names. - -If we want *all* properties: - -- [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) -- returns an array of all own property names. - -These methods are a bit different about which properties they return, but all of them operate on the object itself. Properties from the prototype are not listed. - -The `for..in` loop is different: it loops over inherited properties too. - -For instance: - -```js run -let animal = { - eats: true -}; - -let rabbit = { - jumps: true, - __proto__: animal -}; +## Summary -*!* -// only own keys -alert(Object.keys(rabbit)); // jumps -*/!* +Modern methods to setup and directly access the prototype are: -*!* -// inherited keys too -for(let prop in rabbit) alert(prop); // jumps, then eats -*/!* -``` +- [Object.create(proto[, descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` (can be `null`) and optional property descriptors. +- [Object.getPrototypeOf(obj)](mdn:js/Object.getPrototypeOf) -- returns the `[[Prototype]]` of `obj` (same as `__proto__` getter). +- [Object.setPrototypeOf(obj, proto)](mdn:js/Object.setPrototypeOf) -- sets the `[[Prototype]]` of `obj` to `proto` (same as `__proto__` setter). -If we want to distinguish inherited properties, there's a built-in method [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): it returns `true` if `obj` has its own (not inherited) property named `key`. +The built-in `__proto__` getter/setter is unsafe if we'd want to put user-generated keys in to an object. Just because a user may enter "__proto__" as the key, and there'll be an error with hopefully easy, but generally unpredictable consequences. -So we can filter out inherited properties (or do something else with them): +So we can either use `Object.create(null)` to create a "very plain" object without `__proto__`, or stick to `Map` objects for that. -```js run -let animal = { - eats: true -}; - -let rabbit = { - jumps: true, - __proto__: animal -}; +Also, `Object.create` provides an easy way to shallow-copy an object with all descriptors: -for(let prop in rabbit) { - let isOwn = rabbit.hasOwnProperty(prop); - alert(`${prop}: ${isOwn}`); // jumps:true, then eats:false -} +```js +let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); ``` -Here we have the following inheritance chain: `rabbit`, then `animal`, then `Object.prototype` (because `animal` is a literal object `{...}`, so it's by default), and then `null` above it: - -![](rabbit-animal-object.png) - -Note, there's one funny thing. Where is the method `rabbit.hasOwnProperty` coming from? Looking at the chain we can see that the method is provided by `Object.prototype.hasOwnProperty`. In other words, it's inherited. -...But why `hasOwnProperty` does not appear in `for..in` loop, if it lists all inherited properties? The answer is simple: it's not enumerable. Just like all other properties of `Object.prototype`. That's why they are not listed. -## Summary - -Here's a brief list of methods we discussed in this chapter -- as a recap: - -- [Object.create(proto[, descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` (can be `null`) and optional property descriptors. -- [Object.getPrototypeOf(obj)](mdn:js/Object.getPrototypeOf) -- returns the `[[Prototype]]` of `obj` (same as `__proto__` getter). -- [Object.setPrototypeOf(obj, proto)](mdn:js/Object.setPrototypeOf) -- sets the `[[Prototype]]` of `obj` to `proto` (same as `__proto__` setter). - [Object.keys(obj)](mdn:js/Object/keys) / [Object.values(obj)](mdn:js/Object/values) / [Object.entries(obj)](mdn:js/Object/entries) -- returns an array of enumerable own string property names/values/key-value pairs. - [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) -- returns an array of all own symbolic property names. - [Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) -- returns an array of all own string property names. diff --git a/1-js/08-prototypes/04-prototype-methods/object-prototype-2.png b/1-js/08-prototypes/04-prototype-methods/object-prototype-2.png new file mode 100644 index 000000000..343435af6 Binary files /dev/null and b/1-js/08-prototypes/04-prototype-methods/object-prototype-2.png differ diff --git a/1-js/08-prototypes/04-prototype-methods/object-prototype-2@2x.png b/1-js/08-prototypes/04-prototype-methods/object-prototype-2@2x.png new file mode 100644 index 000000000..86b8a678e Binary files /dev/null and b/1-js/08-prototypes/04-prototype-methods/object-prototype-2@2x.png differ diff --git a/1-js/08-prototypes/04-prototype-methods/object-prototype-null.png b/1-js/08-prototypes/04-prototype-methods/object-prototype-null.png new file mode 100644 index 000000000..792c4f423 Binary files /dev/null and b/1-js/08-prototypes/04-prototype-methods/object-prototype-null.png differ diff --git a/1-js/08-prototypes/04-prototype-methods/object-prototype-null@2x.png b/1-js/08-prototypes/04-prototype-methods/object-prototype-null@2x.png new file mode 100644 index 000000000..d97efa884 Binary files /dev/null and b/1-js/08-prototypes/04-prototype-methods/object-prototype-null@2x.png differ diff --git a/1-js/08-prototypes/05-getting-all-properties/article.md b/1-js/08-prototypes/05-getting-all-properties/article.md new file mode 100644 index 000000000..345181786 --- /dev/null +++ b/1-js/08-prototypes/05-getting-all-properties/article.md @@ -0,0 +1,83 @@ + +# Getting all properties + +There are many ways to get keys/values from an object. + +Most of them operate on the object itself, excluding the prototype, let's recall them: + +- [Object.keys(obj)](mdn:js/Object/keys) / [Object.values(obj)](mdn:js/Object/values) / [Object.entries(obj)](mdn:js/Object/entries) -- returns an array of enumerable own string property names/values/key-value pairs. These methods only list *enumerable* properties, and those that have *strings as keys*. + +If we want symbolic properties: + +- [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) -- returns an array of all own symbolic property names. + +If we want non-enumerable properties: + +- [Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) -- returns an array of all own string property names. + +If we want *all* properties: + +- [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) -- returns an array of all own property names. + +These methods are a bit different about which properties they return, but all of them operate on the object itself. Properties from the prototype are not listed. + +## for..in loop + +The `for..in` loop is different: it loops over inherited properties too. + +For instance: + +```js run +let animal = { + eats: true +}; + +let rabbit = { + jumps: true, + __proto__: animal +}; + +*!* +// only own keys +alert(Object.keys(rabbit)); // jumps +*/!* + +*!* +// inherited keys too +for(let prop in rabbit) alert(prop); // jumps, then eats +*/!* +``` + +If that's not what we want, and we'd like to exclude inherited properties, there's a built-in method [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): it returns `true` if `obj` has its own (not inherited) property named `key`. + +So we can filter out inherited properties (or do something else with them): + +```js run +let animal = { + eats: true +}; + +let rabbit = { + jumps: true, + __proto__: animal +}; + +for(let prop in rabbit) { + let isOwn = rabbit.hasOwnProperty(prop); + alert(`${prop}: ${isOwn}`); // jumps: true, then eats: false +} +``` + +Here we have the following inheritance chain: `rabbit`, then `animal`, then `Object.prototype` (because `animal` is a literal object `{...}`, so it's by default), and then `null` above it: + +![](rabbit-animal-object.png) + +Note, there's one funny thing. Where is the method `rabbit.hasOwnProperty` coming from? Looking at the chain we can see that the method is provided by `Object.prototype.hasOwnProperty`. In other words, it's inherited. + +...But why `hasOwnProperty` does not appear in `for..in` loop, if it lists all inherited properties? The answer is simple: it's not enumerable. Just like all other properties of `Object.prototype`. That's why they are not listed. + +## Summary + +Most methods ignore inherited properties, with a notable exception of `for..in`. + +For the latter we can use [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): it returns `true` if `obj` has its own (not inherited) property named `key`. diff --git a/1-js/08-prototypes/05-getting-all-properties/rabbit-animal-object.png b/1-js/08-prototypes/05-getting-all-properties/rabbit-animal-object.png new file mode 100644 index 000000000..3924233dd Binary files /dev/null and b/1-js/08-prototypes/05-getting-all-properties/rabbit-animal-object.png differ diff --git a/1-js/08-prototypes/05-getting-all-properties/rabbit-animal-object@2x.png b/1-js/08-prototypes/05-getting-all-properties/rabbit-animal-object@2x.png new file mode 100644 index 000000000..5350a1935 Binary files /dev/null and b/1-js/08-prototypes/05-getting-all-properties/rabbit-animal-object@2x.png differ diff --git a/1-js/08-prototypes/index.md b/1-js/08-prototypes/index.md new file mode 100644 index 000000000..8554a0e30 --- /dev/null +++ b/1-js/08-prototypes/index.md @@ -0,0 +1 @@ +# Prototypes, inheritance diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/clock.js b/1-js/09-classes/01-class/1-rewrite-to-class/_js.view/solution.js similarity index 64% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/clock.js rename to 1-js/09-classes/01-class/1-rewrite-to-class/_js.view/solution.js index 8009273e3..0b31cf334 100644 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/clock.js +++ b/1-js/09-classes/01-class/1-rewrite-to-class/_js.view/solution.js @@ -1,9 +1,9 @@ class Clock { constructor({ template }) { - this._template = template; + this.template = template; } - _render() { + render() { let date = new Date(); let hours = date.getHours(); @@ -15,7 +15,7 @@ class Clock { let secs = date.getSeconds(); if (secs < 10) secs = '0' + secs; - let output = this._template + let output = this.template .replace('h', hours) .replace('m', mins) .replace('s', secs); @@ -24,11 +24,15 @@ class Clock { } stop() { - clearInterval(this._timer); + clearInterval(this.timer); } start() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); + this.render(); + this.timer = setInterval(() => this.render(), 1000); } } + + +let clock = new Clock({template: 'h:m:s'}); +clock.start(); diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/clock.js b/1-js/09-classes/01-class/1-rewrite-to-class/_js.view/source.js similarity index 90% rename from 1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/clock.js rename to 1-js/09-classes/01-class/1-rewrite-to-class/_js.view/source.js index c4bfaa0ff..f1749c8ba 100644 --- a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/clock.js +++ b/1-js/09-classes/01-class/1-rewrite-to-class/_js.view/source.js @@ -32,3 +32,6 @@ function Clock({ template }) { }; } + +let clock = new Clock({template: 'h:m:s'}); +clock.start(); diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.md b/1-js/09-classes/01-class/1-rewrite-to-class/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.md rename to 1-js/09-classes/01-class/1-rewrite-to-class/solution.md diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/task.md b/1-js/09-classes/01-class/1-rewrite-to-class/task.md similarity index 53% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/task.md rename to 1-js/09-classes/01-class/1-rewrite-to-class/task.md index a29d347f5..05365e410 100644 --- a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/task.md +++ b/1-js/09-classes/01-class/1-rewrite-to-class/task.md @@ -4,6 +4,6 @@ importance: 5 # Rewrite to class -Rewrite the `Clock` class from prototypes to the modern "class" syntax. +The `Clock` class is written in functional style. Rewrite it the "class" syntax. P.S. The clock ticks in the console, open it to see. diff --git a/1-js/09-classes/01-class/article.md b/1-js/09-classes/01-class/article.md new file mode 100644 index 000000000..16ff9c666 --- /dev/null +++ b/1-js/09-classes/01-class/article.md @@ -0,0 +1,368 @@ + +# Class basic syntax + +```quote author="Wikipedia" +In object-oriented programming, a *class* is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions or methods). +``` + +In practice, we often need to create many objects of the same kind, like users, or goods or whatever. + +As we already know from the chapter , `new function` can help with that. + +But in the modern JavaScript, there's a more advanced "class" construct, that introduces great new features which are useful for object-oriented programming. + +## The "class" syntax + +The basic syntax is: +```js +class MyClass { + // class methods + constructor() { ... } + method1() { ... } + method2() { ... } + method3() { ... } + ... +} +``` + +Then `new MyClass()` creates a new object with all the listed methods. + +The `constructor()` method is called automatically by `new`, so we can initialize the object there. + +For example: + +```js run +class User { + + constructor(name) { + this.name = name; + } + + sayHi() { + alert(this.name); + } + +} + +// Usage: +let user = new User("John"); +user.sayHi(); +``` + +When `new User("John")` is called: +1. A new object is created. +2. The `constructor` runs with the given argument and assigns `this.name` to it. + +...Then we can call methods, such as `user.sayHi`. + + +```warn header="No comma between class methods" +A common pitfall for novice developers is to put a comma between class methods, which would result in a syntax error. + +The notation here is not to be confused with object literals. Within the class, no commas are required. +``` + +## What is a class? + +So, what exactly is a `class`? That's not an entirely new language-level entity, as one might think. + +Let's unveil any magic and see what a class really is. That'll help in understanding many complex aspects. + +In JavaScript, a class is a kind of a function. + +Here, take a look: + +```js run +class User { + constructor(name) { this.name = name; } + sayHi() { alert(this.name); } +} + +// proof: User is a function +*!* +alert(typeof User); // function +*/!* +``` + +What `class User {...}` construct really does is: +1. Creates a function named `User`, that becomes the result of the class declaration. + - The function code is taken from the `constructor` method (assumed empty is we don't write such method). +3. Stores all methods, such as `sayHi`, in `User.prototype`. + +Afterwards, for new objects, when we call a method, it's taken from the prototype, just as described in the chapter . So `new User` object has access to class methods. + +We can illustrate the result of `class User` as: + +![](class-user.png) + +Here's the code to introspect it: + + +```js run +class User { + constructor(name) { this.name = name; } + sayHi() { alert(this.name); } +} + +// class is a function +alert(typeof User); // function + +// ...or, more precisely, the constructor method +alert(User === User.prototype.constructor); // true + +// The methods are in User.prototype, e.g: +alert(User.prototype.sayHi); // alert(this.name); + +// there are exactly two methods in the prototype +alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi +``` + +## Not just a syntax sugar + +Sometimes people say that `class` is a "syntax sugar" in JavaScript, because we could actually declare the same without `class` keyword at all: + +```js run +// rewriting class User in pure functions + +// 1. Create constructor function +function User(name) { + this.name = name; +} +// any function prototype has constructor property by default, +// so we don't need to create it + +// 2. Add the method to prototype +User.prototype.sayHi = function() { + alert(this.name); +}; + +// Usage: +let user = new User("John"); +user.sayHi(); +``` + +The result of this definition is about the same. So, there are indeed reasons why `class` can be considered a syntax sugar to define a constructor together with its prototype methods. + +Although, there are important differences. + +1. First, a function created by `class` is labelled by a special internal property `[[FunctionKind]]:"classConstructor"`. So it's not entirely the same as creating it manually. + + Unlike a regular function, a class constructor can't be called without `new`: + + ```js run + class User { + constructor() {} + } + + alert(typeof User); // function + User(); // Error: Class constructor User cannot be invoked without 'new' + ``` + + Also, a string representation of a class constructor in most JavaScript engines starts with the "class..." + + ```js run + class User { + constructor() {} + } + + alert(User); // class User { ... } + ``` + +2. Class methods are non-enumerable + A class definition sets `enumerable` flag to `false` for all methods in the `"prototype"`. + + That's good, because if we `for..in` over an object, we usually don't want its class methods. + +3. Classes always `use strict` + All code inside the class construct is automatically in strict mode. + + +Also, in addition to its basic operation, the `class` syntax brings many other features with it which we'll explore later. + +## Class Expression + +Just like functions, classes can be defined inside another expression, passed around, returned, assigned etc. + +Here's an example of a class expression: + +```js +let User = class { + sayHi() { + alert("Hello"); + } +}; +``` + +Similar to Named Function Expressions, class expressions may or may not have a name. + +If a class expression has a name, it's visible inside the class only: + +```js run +// "Named Class Expression" (alas, no such term, but that's what's going on) +let User = class *!*MyClass*/!* { + sayHi() { + alert(MyClass); // MyClass is visible only inside the class + } +}; + +new User().sayHi(); // works, shows MyClass definition + +alert(MyClass); // error, MyClass not visible outside of the class +``` + + +We can even make classes dynamically "on-demand", like this: + +```js run +function makeClass(phrase) { + // declare a class and return it + return class { + sayHi() { + alert(phrase); + }; + }; +} + +// Create a new class +let User = makeClass("Hello"); + +new User().sayHi(); // Hello +``` + + +## Getters/setters, other shorthands + +Classes also include getters/setters, generators, computed properties etc. + +Here's an example for `user.name` implemented using `get/set`: + +```js run +class User { + + constructor(name) { + // invokes the setter + this._name = name; + } + +*!* + get name() { +*/!* + return this._name; + } + +*!* + set name(value) { +*/!* + if (value.length < 4) { + alert("Name is too short."); + return; + } + this._name = value; + } + +} + +let user = new User("John"); +alert(user.name); // John + +user = new User(""); // Name too short. +``` + +Internally, getters and setters are created on `User.prototype`, like this: + +```js +Object.defineProperties(User.prototype, { + name: { + get() { + return this._name + }, + set(name) { + // ... + } + } +}); +``` + +Here's an example with computed properties: + +```js run +function f() { return "sayHi"; } + +class User { + [f()]() { + alert("Hello"); + } + +} + +new User().sayHi(); +``` + +For a generator method, similarly, prepend it with `*`. + +## Class properties + +```warn header="Old browsers may need a polyfill" +Class-level properties are a recent addition to the language. +``` + +In the example above, `User` only had methods. Let's add a property: + +```js run +class User { + name = "Anonymous"; + + sayHi() { + alert(`Hello, ${this.name}!`); + } +} + +new User().sayHi(); +``` + +The property is not placed into `User.prototype`. Instead, it is created by `new`, separately for every object. So, the property will never be shared between different objects of the same class. + + +## Summary + +JavaScript provides many ways to create a class. + +First, as per the general object-oriented terminology, a class is something that provides "object templates", allows to create same-structured objects. + +When we say "a class", that doesn't necessary means the `class` keyword. + +This is a class: + +```js +function User(name) { + this.sayHi = function() { + alert(name); + } +} +``` + +...But in most cases `class` keyword is used, as it provides great syntax and many additional features. + +The basic class syntax looks like this: + +```js +class MyClass { + prop = value; // field + + constructor(...) { // constructor + // ... + } + + method(...) {} // method + + get something(...) {} // getter method + set something(...) {} // setter method + + [Symbol.iterator]() {} // method with computed name/symbol name + // ... +} +``` + +`MyClass` is technically a function, while methods are written to `MyClass.prototype`. + +In the next chapters we'll learn more about classes, including inheritance and other features. diff --git a/1-js/09-classes/01-class/class-user.png b/1-js/09-classes/01-class/class-user.png new file mode 100644 index 000000000..dc8b75679 Binary files /dev/null and b/1-js/09-classes/01-class/class-user.png differ diff --git a/1-js/09-classes/01-class/class-user@2x.png b/1-js/09-classes/01-class/class-user@2x.png new file mode 100644 index 000000000..aaa53708f Binary files /dev/null and b/1-js/09-classes/01-class/class-user@2x.png differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/solution.md b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/solution.md rename to 1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/task.md b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/task.md rename to 1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.md b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.md new file mode 100644 index 000000000..dcb4ffe59 --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.md @@ -0,0 +1 @@ +[js src="solution.view/extended-clock.js"] diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/clock.js b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/clock.js similarity index 70% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/clock.js rename to 1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/clock.js index 8009273e3..d701c0cae 100644 --- a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/clock.js +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/clock.js @@ -1,9 +1,9 @@ class Clock { constructor({ template }) { - this._template = template; + this.template = template; } - _render() { + render() { let date = new Date(); let hours = date.getHours(); @@ -15,7 +15,7 @@ class Clock { let secs = date.getSeconds(); if (secs < 10) secs = '0' + secs; - let output = this._template + let output = this.template .replace('h', hours) .replace('m', mins) .replace('s', secs); @@ -24,11 +24,11 @@ class Clock { } stop() { - clearInterval(this._timer); + clearInterval(this.timer); } start() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); + this.render(); + this.timer = setInterval(() => this.render(), 1000); } } diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js similarity index 53% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js rename to 1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js index 4eb12381f..ca613ca5e 100644 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js @@ -2,11 +2,11 @@ class ExtendedClock extends Clock { constructor(options) { super(options); let { precision=1000 } = options; - this._precision = precision; + this.precision = precision; } start() { - this._render(); - this._timer = setInterval(() => this._render(), this._precision); + this.render(); + this.timer = setInterval(() => this.render(), this.precision); } }; diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/index.html b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/index.html new file mode 100644 index 000000000..f76a43623 --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/index.html @@ -0,0 +1,12 @@ + + + + + diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/clock.js b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/clock.js similarity index 70% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/clock.js rename to 1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/clock.js index 8009273e3..d701c0cae 100644 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/clock.js +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/clock.js @@ -1,9 +1,9 @@ class Clock { constructor({ template }) { - this._template = template; + this.template = template; } - _render() { + render() { let date = new Date(); let hours = date.getHours(); @@ -15,7 +15,7 @@ class Clock { let secs = date.getSeconds(); if (secs < 10) secs = '0' + secs; - let output = this._template + let output = this.template .replace('h', hours) .replace('m', mins) .replace('s', secs); @@ -24,11 +24,11 @@ class Clock { } stop() { - clearInterval(this._timer); + clearInterval(this.timer); } start() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); + this.render(); + this.timer = setInterval(() => this.render(), 1000); } } diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html new file mode 100644 index 000000000..c0609858b --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html @@ -0,0 +1,21 @@ + + + diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/task.md b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md similarity index 92% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/task.md rename to 1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md index 05da45387..bbc2c6a43 100644 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/task.md +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md @@ -6,6 +6,9 @@ importance: 5 We've got a `Clock` class. As of now, it prints the time every second. + +[js src="source.view/clock.js"] + Create a new class `ExtendedClock` that inherits from `Clock` and adds the parameter `precision` -- the number of `ms` between "ticks". Should be `1000` (1 second) by default. - Your code should be in the file `extended-clock.js` diff --git a/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.png b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.png new file mode 100644 index 000000000..c610e28c3 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.png differ diff --git a/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png new file mode 100644 index 000000000..4819c7f79 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/solution.md b/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/solution.md rename to 1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/task.md b/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/task.md rename to 1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md diff --git a/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.png b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.png new file mode 100644 index 000000000..0d887dbc0 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.png differ diff --git a/1-js/09-classes/02-class-inheritance/animal-rabbit-extends@2x.png b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends@2x.png new file mode 100644 index 000000000..af09271a9 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends@2x.png differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/article.md b/1-js/09-classes/02-class-inheritance/article.md similarity index 72% rename from 1-js/07-object-oriented-programming/10-class-inheritance/article.md rename to 1-js/09-classes/02-class-inheritance/article.md index 2c44e5ead..7d00d9bd1 100644 --- a/1-js/07-object-oriented-programming/10-class-inheritance/article.md +++ b/1-js/09-classes/02-class-inheritance/article.md @@ -1,7 +1,53 @@ -# Class inheritance, super +# Class inheritance -Classes can extend one another. There's a nice syntax, technically based on the prototypal inheritance. +Let's say we have two classes. + +`Animal`: + +```js +class Animal { + constructor(name) { + this.speed = 0; + this.name = name; + } + run(speed) { + this.speed += speed; + alert(`${this.name} runs with speed ${this.speed}.`); + } + stop() { + this.speed = 0; + alert(`${this.name} stopped.`); + } +} + +let animal = new Animal("My animal"); +``` + +![](rabbit-animal-independent-animal.png) + + +...And `Rabbit`: + +```js +class Rabbit { + constructor(name) { + this.name = name; + } + hide() { + alert(`${this.name} hides!`); + } +} + +let rabbit = new Rabbit("My rabbit"); +``` + +![](rabbit-animal-independent-rabbit.png) + + +Right now they are fully independent. + +But we'd want `Rabbit` to extend `Animal`. In other words, rabbits should be based on animals, have access to methods of `Animal` and extend them with its own methods. To inherit from another class, we should specify `"extends"` and the parent class before the brackets `{..}`. @@ -9,32 +55,28 @@ Here `Rabbit` inherits from `Animal`: ```js run class Animal { - constructor(name) { this.speed = 0; this.name = name; } - run(speed) { this.speed += speed; alert(`${this.name} runs with speed ${this.speed}.`); } - stop() { this.speed = 0; alert(`${this.name} stopped.`); } - } +// Inherit from Animal by specifying "extends Animal" *!* -// Inherit from Animal class Rabbit extends Animal { +*/!* hide() { alert(`${this.name} hides!`); } } -*/!* let rabbit = new Rabbit("White Rabbit"); @@ -42,11 +84,15 @@ rabbit.run(5); // White Rabbit runs with speed 5. rabbit.hide(); // White Rabbit hides! ``` -The `extends` keyword actually adds a `[[Prototype]]` reference from `Rabbit.prototype` to `Animal.prototype`, just as you expect it to be, and as we've seen before. +Now the `Rabbit` code became a bit shorter, as it uses `Animal` constructor by default, and it also can `run`, as animals do. + +Internally, `extends` keyword adds `[[Prototype]]` reference from `Rabbit.prototype` to `Animal.prototype`: ![](animal-rabbit-extends.png) -So now `rabbit` has access both to its own methods and to methods of `Animal`. +So, if a method is not found in `Rabbit.prototype`, JavaScript takes it from `Animal.prototype`. + +As we can recall from the chapter , JavaScript uses the same prototypal inheritance for build-in objects. E.g. `Date.prototype.[[Prototype]]` is `Object.prototype`, so dates have generic object methods. ````smart header="Any expression is allowed after `extends`" Class syntax allows to specify not just a class, but any expression after `extends`. @@ -429,147 +475,3 @@ let rabbit = { rabbit.eat(); // Error calling super (because there's no [[HomeObject]]) */!* ``` - -## Static methods and inheritance - -The `class` syntax supports inheritance for static properties too. - -For instance: - -```js run -class Animal { - - constructor(name, speed) { - this.speed = speed; - this.name = name; - } - - run(speed = 0) { - this.speed += speed; - alert(`${this.name} runs with speed ${this.speed}.`); - } - - static compare(animalA, animalB) { - return animalA.speed - animalB.speed; - } - -} - -// Inherit from Animal -class Rabbit extends Animal { - hide() { - alert(`${this.name} hides!`); - } -} - -let rabbits = [ - new Rabbit("White Rabbit", 10), - new Rabbit("Black Rabbit", 5) -]; - -rabbits.sort(Rabbit.compare); - -rabbits[0].run(); // Black Rabbit runs with speed 5. -``` - -Now we can call `Rabbit.compare` assuming that the inherited `Animal.compare` will be called. - -How does it work? Again, using prototypes. As you might have already guessed, extends also gives `Rabbit` the `[[Prototype]]` reference to `Animal`. - - -![](animal-rabbit-static.png) - -So, `Rabbit` function now inherits from `Animal` function. And `Animal` function normally has `[[Prototype]]` referencing `Function.prototype`, because it doesn't `extend` anything. - -Here, let's check that: - -```js run -class Animal {} -class Rabbit extends Animal {} - -// for static propertites and methods -alert(Rabbit.__proto__ === Animal); // true - -// and the next step is Function.prototype -alert(Animal.__proto__ === Function.prototype); // true - -// that's in addition to the "normal" prototype chain for object methods -alert(Rabbit.prototype.__proto__ === Animal.prototype); -``` - -This way `Rabbit` has access to all static methods of `Animal`. - -### No static inheritance in built-ins - -Please note that built-in classes don't have such static `[[Prototype]]` reference. For instance, `Object` has `Object.defineProperty`, `Object.keys` and so on, but `Array`, `Date` etc do not inherit them. - -Here's the picture structure for `Date` and `Object`: - -![](object-date-inheritance.png) - -Note, there's no link between `Date` and `Object`. Both `Object` and `Date` exist independently. `Date.prototype` inherits from `Object.prototype`, but that's all. - -Such difference exists for historical reasons: there was no thought about class syntax and inheriting static methods at the dawn of JavaScript language. - -## Natives are extendable - -Built-in classes like Array, Map and others are extendable also. - -For instance, here `PowerArray` inherits from the native `Array`: - -```js run -// add one more method to it (can do more) -class PowerArray extends Array { - isEmpty() { - return this.length === 0; - } -} - -let arr = new PowerArray(1, 2, 5, 10, 50); -alert(arr.isEmpty()); // false - -let filteredArr = arr.filter(item => item >= 10); -alert(filteredArr); // 10, 50 -alert(filteredArr.isEmpty()); // false -``` - -Please note one very interesting thing. Built-in methods like `filter`, `map` and others -- return new objects of exactly the inherited type. They rely on the `constructor` property to do so. - -In the example above, -```js -arr.constructor === PowerArray -``` - -So when `arr.filter()` is called, it internally creates the new array of results exactly as `new PowerArray`. And we can keep using its methods further down the chain. - -Even more, we can customize that behavior. The static getter `Symbol.species`, if exists, returns the constructor to use in such cases. - -For example, here due to `Symbol.species` built-in methods like `map`, `filter` will return "normal" arrays: - -```js run -class PowerArray extends Array { - isEmpty() { - return this.length === 0; - } - -*!* - // built-in methods will use this as the constructor - static get [Symbol.species]() { - return Array; - } -*/!* -} - -let arr = new PowerArray(1, 2, 5, 10, 50); -alert(arr.isEmpty()); // false - -// filter creates new array using arr.constructor[Symbol.species] as constructor -let filteredArr = arr.filter(item => item >= 10); - -*!* -// filteredArr is not PowerArray, but Array -*/!* -alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function -``` - -We can use it in more advanced keys to strip extended functionality from resulting values if not needed. Or, maybe, to extend it even further. diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.png b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.png new file mode 100644 index 000000000..8d30622ce Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.png differ diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-array-object@2x.png b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object@2x.png new file mode 100644 index 000000000..00f6bd80d Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object@2x.png differ diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.png b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.png new file mode 100644 index 000000000..f8afbbcd6 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.png differ diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2@2x.png b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2@2x.png new file mode 100644 index 000000000..cf5aa6554 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2@2x.png differ diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.png b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.png new file mode 100644 index 000000000..a6f8964e6 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.png differ diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal@2x.png b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal@2x.png new file mode 100644 index 000000000..2e3f4d7ff Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal@2x.png differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal.png b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-run-animal.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal.png rename to 1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-run-animal.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal@2x.png b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-run-animal@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal@2x.png rename to 1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-run-animal@2x.png diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.png b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.png new file mode 100644 index 000000000..79351a754 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.png differ diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal@2x.png b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal@2x.png new file mode 100644 index 000000000..346574e0c Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal@2x.png differ diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.png b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.png new file mode 100644 index 000000000..3d3b78cca Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.png differ diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit@2x.png b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit@2x.png new file mode 100644 index 000000000..a923d10e5 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit@2x.png differ diff --git a/1-js/09-classes/02-class-inheritance/this-super-loop.png b/1-js/09-classes/02-class-inheritance/this-super-loop.png new file mode 100644 index 000000000..e68ed70e7 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/this-super-loop.png differ diff --git a/1-js/09-classes/02-class-inheritance/this-super-loop@2x.png b/1-js/09-classes/02-class-inheritance/this-super-loop@2x.png new file mode 100644 index 000000000..037d07587 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/this-super-loop@2x.png differ diff --git a/1-js/09-classes/03-static-properties-methods/animal-rabbit-static.png b/1-js/09-classes/03-static-properties-methods/animal-rabbit-static.png new file mode 100644 index 000000000..c5e7e3e49 Binary files /dev/null and b/1-js/09-classes/03-static-properties-methods/animal-rabbit-static.png differ diff --git a/1-js/09-classes/03-static-properties-methods/animal-rabbit-static@2x.png b/1-js/09-classes/03-static-properties-methods/animal-rabbit-static@2x.png new file mode 100644 index 000000000..de434af2c Binary files /dev/null and b/1-js/09-classes/03-static-properties-methods/animal-rabbit-static@2x.png differ diff --git a/1-js/09-classes/03-static-properties-methods/article.md b/1-js/09-classes/03-static-properties-methods/article.md new file mode 100644 index 000000000..73a22a79f --- /dev/null +++ b/1-js/09-classes/03-static-properties-methods/article.md @@ -0,0 +1,226 @@ + +# Static properties and methods + +We can also assign a method to the class function, not to its `"prototype"`. Such methods are called *static*. + +An example: + +```js run +class User { +*!* + static staticMethod() { +*/!* + alert(this === User); + } +} + +User.staticMethod(); // true +``` + +That actually does the same as assigning it as a function property: + +```js +function User() { } + +User.staticMethod = function() { + alert(this === User); +}; +``` + +The value of `this` inside `User.staticMethod()` is the class constructor `User` itself (the "object before dot" rule). + +Usually, static methods are used to implement functions that belong to the class, but not to any particular object of it. + +For instance, we have `Article` objects and need a function to compare them. The natural choice would be `Article.compare`, like this: + +```js run +class Article { + constructor(title, date) { + this.title = title; + this.date = date; + } + +*!* + static compare(articleA, articleB) { + return articleA.date - articleB.date; + } +*/!* +} + +// usage +let articles = [ + new Article("Mind", new Date(2019, 1, 1)), + new Article("Body", new Date(2019, 0, 1)), + new Article("JavaScript", new Date(2019, 11, 1)) +]; + +*!* +articles.sort(Article.compare); +*/!* + +alert( articles[0].title ); // Body +``` + +Here `Article.compare` stands "over" the articles, as a means to compare them. It's not a method of an article, but rather of the whole class. + +Another example would be a so-called "factory" method. Imagine, we need few ways to create an article: + +1. Create by given parameters (`title`, `date` etc). +2. Create an empty article with today's date. +3. ... + +The first way can be implemented by the constructor. And for the second one we can make a static method of the class. + +Like `Article.createTodays()` here: + +```js run +class Article { + constructor(title, date) { + this.title = title; + this.date = date; + } + +*!* + static createTodays() { + // remember, this = Article + return new this("Today's digest", new Date()); + } +*/!* +} + +let article = Article.createTodays(); + +alert( article.title ); // Todays digest +``` + +Now every time we need to create a today's digest, we can call `Article.createTodays()`. Once again, that's not a method of an article, but a method of the whole class. + +Static methods are also used in database-related classes to search/save/remove entries from the database, like this: + +```js +// assuming Article is a special class for managing articles +// static method to remove the article: +Article.remove({id: 12345}); +``` + +## Static properties + +[recent browser=Chrome] + +Static properties are also possible, just like regular class properties: + +```js run +class Article { + static publisher = "Ilya Kantor"; +} + +alert( Article.publisher ); // Ilya Kantor +``` + +That is the same as a direct assignment to `Article`: + +```js +Article.publisher = "Ilya Kantor"; +``` + +## Statics and inheritance + +Statics are inherited, we can access `Parent.method` as `Child.method`. + +For instance, `Animal.compare` in the code below is inherited and accessible as `Rabbit.compare`: + +```js run +class Animal { + + constructor(name, speed) { + this.speed = speed; + this.name = name; + } + + run(speed = 0) { + this.speed += speed; + alert(`${this.name} runs with speed ${this.speed}.`); + } + +*!* + static compare(animalA, animalB) { + return animalA.speed - animalB.speed; + } +*/!* + +} + +// Inherit from Animal +class Rabbit extends Animal { + hide() { + alert(`${this.name} hides!`); + } +} + +let rabbits = [ + new Rabbit("White Rabbit", 10), + new Rabbit("Black Rabbit", 5) +]; + +*!* +rabbits.sort(Rabbit.compare); +*/!* + +rabbits[0].run(); // Black Rabbit runs with speed 5. +``` + +Now we can call `Rabbit.compare` assuming that the inherited `Animal.compare` will be called. + +How does it work? Again, using prototypes. As you might have already guessed, extends also gives `Rabbit` the `[[Prototype]]` reference to `Animal`. + + +![](animal-rabbit-static.png) + +So, `Rabbit` function now inherits from `Animal` function. And `Animal` function normally has `[[Prototype]]` referencing `Function.prototype`, because it doesn't `extend` anything. + +Here, let's check that: + +```js run +class Animal {} +class Rabbit extends Animal {} + +// for static properties and methods +alert(Rabbit.__proto__ === Animal); // true + +// and the next step is Function.prototype +alert(Animal.__proto__ === Function.prototype); // true + +// that's in addition to the "normal" prototype chain for object methods +alert(Rabbit.prototype.__proto__ === Animal.prototype); +``` + +This way `Rabbit` has access to all static methods of `Animal`. + +## Summary + +Static methods are used for the functionality that doesn't relate to a concrete class instance, doesn't require an instance to exist, but rather belongs to the class as a whole, like `Article.compare` -- a generic method to compare two articles. + +Static properties are used when we'd like to store class-level data, also not bound to an instance. + +The syntax is: + +```js +class MyClass { + static property = ...; + + static method() { + ... + } +} +``` + +That's technically the same as assigning to the class itself: + +```js +MyClass.property = ... +MyClass.method = ... +``` + +Static properties are inherited. + +Technically, for `class B extends A` the prototype of the class `B` itself points to `A`: `B.[[Prototype]] = A`. So if a field is not found in `B`, the search continues in `A`. diff --git a/1-js/09-classes/04-private-protected-properties-methods/article.md b/1-js/09-classes/04-private-protected-properties-methods/article.md new file mode 100644 index 000000000..6cd9cc5fb --- /dev/null +++ b/1-js/09-classes/04-private-protected-properties-methods/article.md @@ -0,0 +1,330 @@ + +# Private and protected properties and methods + +One of the most important principles of object oriented programming -- delimiting internal interface from the external one. + +That is "a must" practice in developing anything more complex than a "hello world" app. + +To understand this, let's break away from development and turn our eyes into the real world. + +Usually, devices that we're using are quite complex. But delimiting the internal interface from the external one allows to use them without problems. + +## A real-life example + +For instance, a coffee machine. Simple from outside: a button, a display, a few holes...And, surely, the result -- great coffee! :) + +![](coffee.jpg) + +But inside... (a picture from the repair manual) + +![](coffee-inside.jpg) + +A lot of details. But we can use it without knowing anything. + +Coffee machines are quite reliable, aren't they? We can use one for years, and only if something goes wrong -- bring it for repairs. + +The secret of reliability and simplicity of a coffee machine -- all details are well-tuned and *hidden* inside. + +If we remove the protective cover from the coffee machine, then using it will be much more complex (where to press?), and dangerous (it can electrocute). + +As we'll see, in programming objects are like coffee machines. + +But in order to hide inner details, we'll use not a protective cover, but rather special syntax of the language and conventions. + +## Internal and external interface + +In object-oriented programming, properties and methods are split into two groups: + +- *Internal interface* -- methods and properties, accessible from other methods of the class, but not from the outside. +- *External interface* -- methods and properties, accessible also from outside the class. + +If we continue the analogy with the coffee machine -- what's hidden inside: a boiler tube, heating element, and so on -- is its internal interface. + +An internal interface is used for the object to work, its details use each other. For instance, a boiler tube is attached to the heating element. + +But from the outside a coffee machine is closed by the protective cover, so that no one can reach those. Details are hidden and inaccessible. We can use its features via the external interface. + +So, all we need to use an object is to know its external interface. We may be completely unaware how it works inside, and that's great. + +That was a general introduction. + +In JavaScript, there are three types of properties and members: + +- Public: accessible from anywhere. They comprise the external interface. Till now we were only using public properties and methods. +- Private: accessible only from inside the class. These are for the internal interface. + +In many other languages there also exist "protected" fields: accessible only from inside the class and those extending it. They are also useful for the internal interface. They are in a sense more widespread than private ones, because we usually want inheriting classes to gain access to properly do the extension. + +Protected fields are not implemented in JavaScript on the language level, but in practice they are very convenient, so they are emulated. + +In the next step we'll make a coffee machine in JavaScript with all these types of properties. A coffee machine has a lot of details, we won't model them to stay simple (though we could). + +## Protecting "waterAmount" + +Let's make a simple coffee machine class first: + +```js run +class CoffeeMachine { + waterAmount = 0; // the amount of water inside + + constructor(power) { + this.power = power; + alert( `Created a coffee-machine, power: ${power}` ); + } + +} + +// create the coffee machine +let coffeeMachine = new CoffeeMachine(100); + +// add water +coffeeMachine.waterAmount = 200; +``` + +Right now the properties `waterAmount` and `power` are public. We can easily get/set them from the outside to any value. + +Let's change `waterAmount` property to protected to have more control over it. For instance, we don't want anyone to set it below zero. + +**Protected properties are usually prefixed with an underscore `_`.** + +That is not enforced on the language level, but there's a convention that such properties and methods should not be accessed from the outside. Most programmers follow it. + +So our property will be called `_waterAmount`: + +```js run +class CoffeeMachine { + _waterAmount = 0; + + set waterAmount(value) { + if (value < 0) throw new Error("Negative water"); + this._waterAmount = value; + } + + get waterAmount() { + return this._waterAmount; + } + + constructor(power) { + this._power = power; + } + +} + +// create the coffee machine +let coffeeMachine = new CoffeeMachine(100); + +// add water +coffeeMachine.waterAmount = -10; // Error: Negative water +``` + +Now the access is under control, so setting the water below zero fails. + +## Read-only "power" + +For `power` property, let's make it read-only. It sometimes happens that a property must be set at creation time only, and then never modified. + +That's exactly the case for a coffee machine: power never changes. + +To do so, we only need to make getter, but not the setter: + +```js run +class CoffeeMachine { + // ... + + constructor(power) { + this._power = power; + } + + get power() { + return this._power; + } + +} + +// create the coffee machine +let coffeeMachine = new CoffeeMachine(100); + +alert(`Power is: ${coffeeMachine.power}W`); // Power is: 100W + +coffeeMachine.power = 25; // Error (no setter) +``` + +````smart header="Getter/setter functions" +Here we used getter/setter syntax. + +But most of the time `get.../set...` functions are preferred, like this: + +```js +class CoffeeMachine { + _waterAmount = 0; + + *!*setWaterAmount(value)*/!* { + if (value < 0) throw new Error("Negative water"); + this._waterAmount = value; + } + + *!*getWaterAmount()*/!* { + return this.waterAmount; + } +} + +new CoffeeMachine().setWaterAmount(100); +``` + +That looks a bit longer, but functions are more flexible. They can accept multiple arguments (even if we don't need them right now). So, for the future, just in case we need to refactor something, functions are a safer choise. + +Surely, there's a tradeoff. On the other hand, get/set syntax is shorter, so ultimately there's no strict rule, it's up to you to decide. +```` + +```smart header="Protected fields are inherited" +If we inherit `class MegaMachine extends CoffeeMachine`, then nothing prevents us from accessing `this._waterAmount` or `this._power` from the methods of the new class. + +So protected fields are naturally inheritable. Unlike private ones that we'll see below. +``` + +## Private "#waterLimit" + +[recent browser=none] + +There's a finished JavaScript proposal, almost in the standard, that provides language-level support for private properties and methods. + +Privates should start with `#`. They are only accessible from inside the class. + +For instance, here we add a private `#waterLimit` property and extract the water-checking logic into a separate method: + +```js +class CoffeeMachine { +*!* + #waterLimit = 200; +*/!* + +*!* + #checkWater(value) { + if (value < 0) throw new Error("Negative water"); + if (value > this.#waterLimit) throw new Error("Too much water"); + } +*/!* + + _waterAmount = 0; + + set waterAmount(value) { +*!* + this.#checkWater(value); +*/!* + this._waterAmount = value; + } + + get waterAmount() { + return this.waterAmount; + } + +} + +let coffeeMachine = new CoffeeMachine(); + +*!* +coffeeMachine.#checkWater(); // Error +coffeeMachine.#waterLimit = 1000; // Error +*/!* + +coffeeMachine.waterAmount = 100; // Works +``` + +On the language level, `#` is a special sign that the field is private. We can't access it from outside or from inheriting classes. + +Private fields do not conflict with public ones. We can have both private `#waterAmount` and public `waterAmount` fields at the same time. + +For instance, let's make `waterAmount` an accessor for `#waterAmount`: + +```js run +class CoffeeMachine { + + #waterAmount = 0; + + get waterAmount() { + return this.#waterAmount; + } + + set waterAmount(value) { + if (value < 0) throw new Error("Negative water"); + this.#waterAmount = value; + } +} + +let machine = new CoffeeMachine(); + +machine.waterAmount = 100; +alert(machine.#waterAmount); // Error +``` + +Unlike protected ones, private fields are enforced by the language itself. That's a good thing. + +But if we inherit from `CoffeeMachine`, then we'll have no direct access to `#waterAmount`. We'll need to rely on `waterAmount` getter/setter: + +```js +class CoffeeMachine extends CoffeeMachine() { + method() { +*!* + alert( this.#waterAmount ); // Error: can only access from CoffeeMachine +*/!* + } +} +``` + +In many scenarios such limitation is too severe. If we extend a `CoffeeMachine`, we may have legitimate reason to access its internals. That's why protected fields are used most of the time, even though they are not supported by the language syntax. + +````warn +Private fields are special. + +Remember, usually we can access fields by this[name]: + +```js +class User { + ... + sayHi() { + let fieldName = "name"; + alert(`Hello, ${this[fieldName]}`); + } +} +``` + +With private fields that's impossible: `this['#name']` doesn't work. That's a syntax limitation to ensure privacy. +```` + +## Summary + +In terms of OOP, delimiting of the internal interface from the external one is called [encapsulation]("https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)"). + +It gives the following benefits: + +Protection for users, so that they don't shoot themselves in the feet +: Imagine, there's a team of developers using a coffee machine. It was made by the "Best CoffeeMachine" company, and works fine, but a protective cover was removed. So the internal interface is exposed. + + All developers are civilized -- they use the coffee machine as intended. But one of them, John, decided that he's the smartest one, and made some tweaks in the coffee machine internals. So the coffee machine failed two days later. + + That's surely not John's fault, but rather the person who removed the protective cover and let John do his manipulations. + + The same in programming. If a user of a class will change things not intended to be changed from the outside -- the consequences are unpredictable. + +Supportable +: The situation in programming is more complex than with a real-life coffee machine, because we don't just buy it once. The code constantly undergoes development and improvement. + + **If we strictly delimit the internal interface, then the developer of the class can freely change its internal properties and methods, even without informing the users..** + + It's much easier to develop, if you know that certain methods can be renamed, their parameters can be changed, and even removed, because no external code depends on them. + + For users, when a new version comes out, it may be a total overhaul, but still simple to upgrade if the external interface is the same. + +Hiding complexity +: People adore to use things that are simple. At least from outside. What's inside is a different thing. + + Programmers are not an exception. + + **It's always convenient when implementation details are hidden, and a simple, well-documented external interface is available.** + +To hide internal interface we use either protected or public properties: + +- Protected fields start with `_`. That's a well-known convention, not enforced at the language level. Programmers should only access a field starting with `_` from its class and classes inheriting from it. +- Private fields start with `#`. JavaScript makes sure we only can access those from inside the class. + +Right now, private fields are not well-supported among browsers, but can be polyfilled. diff --git a/1-js/09-classes/04-private-protected-properties-methods/coffee-inside.jpg b/1-js/09-classes/04-private-protected-properties-methods/coffee-inside.jpg new file mode 100644 index 000000000..60f84664d Binary files /dev/null and b/1-js/09-classes/04-private-protected-properties-methods/coffee-inside.jpg differ diff --git a/1-js/09-classes/04-private-protected-properties-methods/coffee.jpg b/1-js/09-classes/04-private-protected-properties-methods/coffee.jpg new file mode 100644 index 000000000..ee26e1c06 Binary files /dev/null and b/1-js/09-classes/04-private-protected-properties-methods/coffee.jpg differ diff --git a/1-js/09-classes/05-extend-natives/article.md b/1-js/09-classes/05-extend-natives/article.md new file mode 100644 index 000000000..63ea96cb5 --- /dev/null +++ b/1-js/09-classes/05-extend-natives/article.md @@ -0,0 +1,82 @@ + +# Extending built-in classes + +Built-in classes like Array, Map and others are extendable also. + +For instance, here `PowerArray` inherits from the native `Array`: + +```js run +// add one more method to it (can do more) +class PowerArray extends Array { + isEmpty() { + return this.length === 0; + } +} + +let arr = new PowerArray(1, 2, 5, 10, 50); +alert(arr.isEmpty()); // false + +let filteredArr = arr.filter(item => item >= 10); +alert(filteredArr); // 10, 50 +alert(filteredArr.isEmpty()); // false +``` + +Please note a very interesting thing. Built-in methods like `filter`, `map` and others -- return new objects of exactly the inherited type. They rely on the `constructor` property to do so. + +In the example above, +```js +arr.constructor === PowerArray +``` + +So when `arr.filter()` is called, it internally creates the new array of results exactly as `new PowerArray`. +That's actually very cool, because we can keep using `PowerArray` methods further on the result. + +Even more, we can customize that behavior. + +There's a special static getter `Symbol.species`, if exists, it returns the constructor to use in such cases. + +If we'd like built-in methods like `map`, `filter` will return regular arrays, we can return `Array` in `Symbol.species`, like here: + +```js run +class PowerArray extends Array { + isEmpty() { + return this.length === 0; + } + +*!* + // built-in methods will use this as the constructor + static get [Symbol.species]() { + return Array; + } +*/!* +} + +let arr = new PowerArray(1, 2, 5, 10, 50); +alert(arr.isEmpty()); // false + +// filter creates new array using arr.constructor[Symbol.species] as constructor +let filteredArr = arr.filter(item => item >= 10); + +*!* +// filteredArr is not PowerArray, but Array +*/!* +alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function +``` + +As you can see, now `.filter` returns `Array`. So the extended functionality is not passed any further. + +## No static inheritance in built-ins + +Built-in objects have their own static methods, for instance `Object.keys`, `Array.isArray` etc. + +And we've already been talking about native classes extending each other: `Array.[[Prototype]] = Object`. + +But statics are an exception. Built-in classes don't inherit static properties from each other. + +In other words, the prototype of built-in constructor `Array` does not point to `Object`. This way `Array` and `Date` do not have `Array.keys` or `Date.keys`. And that feels natural. + +Here's the picture structure for `Date` and `Object`: + +![](object-date-inheritance.png) + +Note, there's no link between `Date` and `Object`. Both `Object` and `Date` exist independently. `Date.prototype` inherits from `Object.prototype`, but that's all. diff --git a/1-js/09-classes/05-extend-natives/object-date-inheritance.png b/1-js/09-classes/05-extend-natives/object-date-inheritance.png new file mode 100644 index 000000000..73020a49e Binary files /dev/null and b/1-js/09-classes/05-extend-natives/object-date-inheritance.png differ diff --git a/1-js/09-classes/05-extend-natives/object-date-inheritance@2x.png b/1-js/09-classes/05-extend-natives/object-date-inheritance@2x.png new file mode 100644 index 000000000..6520f9e26 Binary files /dev/null and b/1-js/09-classes/05-extend-natives/object-date-inheritance@2x.png differ diff --git a/1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/solution.md b/1-js/09-classes/06-instanceof/1-strange-instanceof/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/solution.md rename to 1-js/09-classes/06-instanceof/1-strange-instanceof/solution.md diff --git a/1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/task.md b/1-js/09-classes/06-instanceof/1-strange-instanceof/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/task.md rename to 1-js/09-classes/06-instanceof/1-strange-instanceof/task.md diff --git a/1-js/07-object-oriented-programming/11-instanceof/article.md b/1-js/09-classes/06-instanceof/article.md similarity index 100% rename from 1-js/07-object-oriented-programming/11-instanceof/article.md rename to 1-js/09-classes/06-instanceof/article.md diff --git a/1-js/09-classes/06-instanceof/instanceof.png b/1-js/09-classes/06-instanceof/instanceof.png new file mode 100644 index 000000000..ad0fc77ab Binary files /dev/null and b/1-js/09-classes/06-instanceof/instanceof.png differ diff --git a/1-js/09-classes/06-instanceof/instanceof@2x.png b/1-js/09-classes/06-instanceof/instanceof@2x.png new file mode 100644 index 000000000..c25b166b3 Binary files /dev/null and b/1-js/09-classes/06-instanceof/instanceof@2x.png differ diff --git a/1-js/07-object-oriented-programming/13-mixins/article.md b/1-js/09-classes/07-mixins/article.md similarity index 100% rename from 1-js/07-object-oriented-programming/13-mixins/article.md rename to 1-js/09-classes/07-mixins/article.md diff --git a/1-js/07-object-oriented-programming/13-mixins/head.html b/1-js/09-classes/07-mixins/head.html similarity index 100% rename from 1-js/07-object-oriented-programming/13-mixins/head.html rename to 1-js/09-classes/07-mixins/head.html diff --git a/1-js/09-classes/07-mixins/mixin-inheritance.png b/1-js/09-classes/07-mixins/mixin-inheritance.png new file mode 100644 index 000000000..6142ce7fc Binary files /dev/null and b/1-js/09-classes/07-mixins/mixin-inheritance.png differ diff --git a/1-js/09-classes/07-mixins/mixin-inheritance@2x.png b/1-js/09-classes/07-mixins/mixin-inheritance@2x.png new file mode 100644 index 000000000..ccbd74300 Binary files /dev/null and b/1-js/09-classes/07-mixins/mixin-inheritance@2x.png differ diff --git a/1-js/09-classes/index.md b/1-js/09-classes/index.md new file mode 100644 index 000000000..87846ef6b --- /dev/null +++ b/1-js/09-classes/index.md @@ -0,0 +1 @@ +# Classes diff --git a/1-js/08-error-handling/1-try-catch/1-finally-or-code-after/solution.md b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md similarity index 100% rename from 1-js/08-error-handling/1-try-catch/1-finally-or-code-after/solution.md rename to 1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md diff --git a/1-js/08-error-handling/1-try-catch/1-finally-or-code-after/task.md b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md similarity index 100% rename from 1-js/08-error-handling/1-try-catch/1-finally-or-code-after/task.md rename to 1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md diff --git a/1-js/08-error-handling/1-try-catch/article.md b/1-js/10-error-handling/1-try-catch/article.md similarity index 98% rename from 1-js/08-error-handling/1-try-catch/article.md rename to 1-js/10-error-handling/1-try-catch/article.md index ac3e0031b..797293582 100644 --- a/1-js/08-error-handling/1-try-catch/article.md +++ b/1-js/10-error-handling/1-try-catch/article.md @@ -115,7 +115,7 @@ To catch an exception inside a scheduled function, `try..catch` must be inside t setTimeout(function() { try { noSuchVariable; // try..catch handles the error! - } catch (e) { + } catch { alert( "error is caught here!" ); } }, 1000); @@ -165,6 +165,19 @@ try { } ``` +## Optional "catch" binding + +[recent browser=new] + +If we don't need error details, `catch` may omit it: + +```js +try { + // ... +} catch { + // error object omitted +} +``` ## Using "try..catch" @@ -577,7 +590,7 @@ Let's imagine we've got a fatal error outside of `try..catch`, and the script di Is there a way to react on such occurrences? We may want to log the error, show something to the user (normally they don't see error messages) etc. -There is none in the specification, but environments usually provide it, because it's really useful. For instance, Node.JS has [process.on('uncaughtException')](https://nodejs.org/api/process.html#process_event_uncaughtexception) for that. And in the browser we can assign a function to special [window.onerror](mdn:api/GlobalEventHandlers/onerror) property. It will run in case of an uncaught error. +There is none in the specification, but environments usually provide it, because it's really useful. For instance, Node.js has [process.on('uncaughtException')](https://nodejs.org/api/process.html#process_event_uncaughtexception) for that. And in the browser we can assign a function to special [window.onerror](mdn:api/GlobalEventHandlers/onerror) property. It will run in case of an uncaught error. The syntax: @@ -653,6 +666,8 @@ Error objects have following properties: - `name` -- the string with error name (error constructor name). - `stack` (non-standard) -- the stack at the moment of error creation. +If error is not needed, we can omit it by using `catch {` instead of `catch(err) {`. + We can also generate our own errors using the `throw` operator. Technically, the argument of `throw` can be anything, but usually it's an error object inheriting from the built-in `Error` class. More on extending errors in the next chapter. Rethrowing is a basic pattern of error handling: a `catch` block usually expects and knows how to handle the particular error type, so it should rethrow errors it doesn't know. diff --git a/1-js/10-error-handling/1-try-catch/try-catch-flow.png b/1-js/10-error-handling/1-try-catch/try-catch-flow.png new file mode 100644 index 000000000..8b3775381 Binary files /dev/null and b/1-js/10-error-handling/1-try-catch/try-catch-flow.png differ diff --git a/1-js/10-error-handling/1-try-catch/try-catch-flow@2x.png b/1-js/10-error-handling/1-try-catch/try-catch-flow@2x.png new file mode 100644 index 000000000..f85eb6321 Binary files /dev/null and b/1-js/10-error-handling/1-try-catch/try-catch-flow@2x.png differ diff --git a/1-js/08-error-handling/2-custom-errors/1-format-error/solution.md b/1-js/10-error-handling/2-custom-errors/1-format-error/solution.md similarity index 100% rename from 1-js/08-error-handling/2-custom-errors/1-format-error/solution.md rename to 1-js/10-error-handling/2-custom-errors/1-format-error/solution.md diff --git a/1-js/08-error-handling/2-custom-errors/1-format-error/task.md b/1-js/10-error-handling/2-custom-errors/1-format-error/task.md similarity index 100% rename from 1-js/08-error-handling/2-custom-errors/1-format-error/task.md rename to 1-js/10-error-handling/2-custom-errors/1-format-error/task.md diff --git a/1-js/08-error-handling/2-custom-errors/article.md b/1-js/10-error-handling/2-custom-errors/article.md similarity index 96% rename from 1-js/08-error-handling/2-custom-errors/article.md rename to 1-js/10-error-handling/2-custom-errors/article.md index d7d61166d..5079c746d 100644 --- a/1-js/08-error-handling/2-custom-errors/article.md +++ b/1-js/10-error-handling/2-custom-errors/article.md @@ -19,7 +19,7 @@ let json = `{ "name": "John", "age": 30 }`; Internally, we'll use `JSON.parse`. If it receives malformed `json`, then it throws `SyntaxError`. -But even if `json` is syntactically correct, that doesn't mean that it's a valid user, right? It may miss the necessary data. For instance, if may not have `name` and `age` properties that are essential for our users. +But even if `json` is syntactically correct, that doesn't mean that it's a valid user, right? It may miss the necessary data. For instance, it may not have `name` and `age` properties that are essential for our users. Our function `readUser(json)` will not only read JSON, but check ("validate") the data. If there are no required fields, or the format is wrong, then that's an error. And that's not a `SyntaxError`, because the data is syntactically correct, but another kind of error. We'll call it `ValidationError` and create a class for it. An error of that kind should also carry the information about the offending field. @@ -185,7 +185,7 @@ try { The new class `PropertyRequiredError` is easy to use: we only need to pass the property name: `new PropertyRequiredError(property)`. The human-readable `message` is generated by the constructor. -Please note that `this.name` in `PropertyRequiredError` constructor is again assigned manually. That may become a bit tedius -- to assign `this.name = ` when creating each custom error. But there's a way out. We can make our own "basic error" class that removes this burden from our shoulders by using `this.constructor.name` for `this.name` in the constructor. And then inherit from it. +Please note that `this.name` in `PropertyRequiredError` constructor is again assigned manually. That may become a bit tedious -- to assign `this.name = ` when creating each custom error. But there's a way out. We can make our own "basic error" class that removes this burden from our shoulders by using `this.constructor.name` for `this.name` in the constructor. And then inherit from it. Let's call it `MyError`. diff --git a/1-js/08-error-handling/index.md b/1-js/10-error-handling/index.md similarity index 100% rename from 1-js/08-error-handling/index.md rename to 1-js/10-error-handling/index.md diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.md b/1-js/11-async/01-callbacks/01-animate-circle-callback/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.md rename to 1-js/11-async/01-callbacks/01-animate-circle-callback/solution.md diff --git a/6-async/01-callbacks/01-animate-circle-callback/solution.view/index.html b/1-js/11-async/01-callbacks/01-animate-circle-callback/solution.view/index.html similarity index 100% rename from 6-async/01-callbacks/01-animate-circle-callback/solution.view/index.html rename to 1-js/11-async/01-callbacks/01-animate-circle-callback/solution.view/index.html diff --git a/6-async/01-callbacks/01-animate-circle-callback/task.md b/1-js/11-async/01-callbacks/01-animate-circle-callback/task.md similarity index 100% rename from 6-async/01-callbacks/01-animate-circle-callback/task.md rename to 1-js/11-async/01-callbacks/01-animate-circle-callback/task.md diff --git a/6-async/01-callbacks/article.md b/1-js/11-async/01-callbacks/article.md similarity index 94% rename from 6-async/01-callbacks/article.md rename to 1-js/11-async/01-callbacks/article.md index a5a91793e..ee0bb3caa 100644 --- a/6-async/01-callbacks/article.md +++ b/1-js/11-async/01-callbacks/article.md @@ -98,7 +98,7 @@ Here we did it in `loadScript`, but of course, it's a general approach. ## Callback in callback -How to load two scripts sequentially: the first one, and then the second one after it? +How can we load two scripts sequentially: the first one, and then the second one after it? The natural solution would be to put the second `loadScript` call inside the callback, like this: @@ -140,7 +140,7 @@ So, every new action is inside a callback. That's fine for few actions, but not ## Handling errors -In examples above we didn't consider errors. What if the script loading fails? Our callback should be able to react on that. +In the above examples we didn't consider errors. What if the script loading fails? Our callback should be able to react on that. Here's an improved version of `loadScript` that tracks loading errors: @@ -262,7 +262,7 @@ function step3(error, script) { See? It does the same, and there's no deep nesting now because we made every action a separate top-level function. -It works, but the code looks like a torn apart spreadsheet. It's difficult to read, and you probably noticed that. One needs to eye-jump between pieces while reading it. That's inconvenient, especially if the reader is not familiar with the code and doesn't know where to eye-jump. +It works, but the code looks like a torn apart spreadsheet. It's difficult to read, and you probably noticed that one needs to eye-jump between pieces while reading it. That's inconvenient, especially if the reader is not familiar with the code and doesn't know where to eye-jump. Also, the functions named `step*` are all of single use, they are created only to avoid the "pyramid of doom." No one is going to reuse them outside of the action chain. So there's a bit of a namespace cluttering here. diff --git a/1-js/11-async/01-callbacks/callback-hell.png b/1-js/11-async/01-callbacks/callback-hell.png new file mode 100644 index 000000000..567ec284e Binary files /dev/null and b/1-js/11-async/01-callbacks/callback-hell.png differ diff --git a/1-js/11-async/01-callbacks/callback-hell@2x.png b/1-js/11-async/01-callbacks/callback-hell@2x.png new file mode 100644 index 000000000..4d6b0ad70 Binary files /dev/null and b/1-js/11-async/01-callbacks/callback-hell@2x.png differ diff --git a/6-async/01-callbacks/one.js b/1-js/11-async/01-callbacks/one.js similarity index 100% rename from 6-async/01-callbacks/one.js rename to 1-js/11-async/01-callbacks/one.js diff --git a/6-async/02-promise-basics/01-re-resolve/solution.md b/1-js/11-async/02-promise-basics/01-re-resolve/solution.md similarity index 100% rename from 6-async/02-promise-basics/01-re-resolve/solution.md rename to 1-js/11-async/02-promise-basics/01-re-resolve/solution.md diff --git a/6-async/02-promise-basics/01-re-resolve/task.md b/1-js/11-async/02-promise-basics/01-re-resolve/task.md similarity index 100% rename from 6-async/02-promise-basics/01-re-resolve/task.md rename to 1-js/11-async/02-promise-basics/01-re-resolve/task.md diff --git a/6-async/02-promise-basics/02-delay-promise/solution.md b/1-js/11-async/02-promise-basics/02-delay-promise/solution.md similarity index 100% rename from 6-async/02-promise-basics/02-delay-promise/solution.md rename to 1-js/11-async/02-promise-basics/02-delay-promise/solution.md diff --git a/6-async/02-promise-basics/02-delay-promise/task.md b/1-js/11-async/02-promise-basics/02-delay-promise/task.md similarity index 100% rename from 6-async/02-promise-basics/02-delay-promise/task.md rename to 1-js/11-async/02-promise-basics/02-delay-promise/task.md diff --git a/3-animation/2-css-animations/3-animate-circle/solution.md b/1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.md similarity index 100% rename from 3-animation/2-css-animations/3-animate-circle/solution.md rename to 1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.md diff --git a/6-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html b/1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html similarity index 100% rename from 6-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html rename to 1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html diff --git a/6-async/02-promise-basics/03-animate-circle-promise/task.md b/1-js/11-async/02-promise-basics/03-animate-circle-promise/task.md similarity index 100% rename from 6-async/02-promise-basics/03-animate-circle-promise/task.md rename to 1-js/11-async/02-promise-basics/03-animate-circle-promise/task.md diff --git a/6-async/02-promise-basics/article.md b/1-js/11-async/02-promise-basics/article.md similarity index 70% rename from 6-async/02-promise-basics/article.md rename to 1-js/11-async/02-promise-basics/article.md index 6f0ebe704..c4bb84eb1 100644 --- a/6-async/02-promise-basics/article.md +++ b/1-js/11-async/02-promise-basics/article.md @@ -48,8 +48,8 @@ Here's an example of a Promise constructor and a simple executor function with i let promise = new Promise(function(resolve, reject) { // the function is executed automatically when the promise is constructed - // after 1 second signal that the job is done with the result "done!" - setTimeout(() => *!*resolve("done!")*/!*, 1000); + // after 1 second signal that the job is done with the result "done" + setTimeout(() => *!*resolve("done")*/!*, 1000); }); ``` @@ -80,7 +80,7 @@ To summarize, the executor should do a job (something that takes time usually) a The Promise that is either resolved or rejected is called "settled", as opposed to a "pending" Promise. ````smart header="There can be only a single result or an error" -The executor should call only one `resolve` or `reject`. The promise's state change is final. +The executor should call only one `resolve` or one `reject`. The promise's state change is final. All further calls of `resolve` and `reject` are ignored: @@ -95,11 +95,11 @@ let promise = new Promise(function(resolve, reject) { The idea is that a job done by the executor may have only one result or an error. -Further, `resolve`/`reject` expect only one argument and will ignore additional arguments. +Also, `resolve`/`reject` expect only one argument (or none) and will ignore additional arguments. ```` ```smart header="Reject with `Error` objects" -In case if something goes wrong, we can call `reject` with any type of argument (just like `resolve`). But it is recommended to use `Error` objects (or objects that inherit from `Error`). The reasoning for that will soon become apparent. +In case something goes wrong, we can call `reject` with any type of argument (just like `resolve`). But it is recommended to use `Error` objects (or objects that inherit from `Error`). The reasoning for that will soon become apparent. ``` ````smart header="Immediately calling `resolve`/`reject`" @@ -118,14 +118,18 @@ That's fine. We immediately have a resolved Promise, nothing wrong with that. ```` ```smart header="The `state` and `result` are internal" -The properties `state` and `result` of the Promise object are internal. We can't directly access them from our "consuming code". We can use the methods `.then`/`.catch` for that. They are described below. +The properties `state` and `result` of the Promise object are internal. We can't directly access them from our "consuming code". We can use the methods `.then`/`.catch`/`.finally` for that. They are described below. ``` -## Consumers: "then" and "catch" +## Consumers: then, catch, finally -A Promise object serves as a link between the executor (the "producing code" or "singer") and the consuming functions (the "fans"), which will receive the result or error. Consuming functions can be registered (subscribed) using the methods `.then` and `.catch`. +A Promise object serves as a link between the executor (the "producing code" or "singer") and the consuming functions (the "fans"), which will receive the result or error. Consuming functions can be registered (subscribed) using methods `.then`, `.catch` and `.finally`. -The syntax of `.then` is: +### then + +The most important, fundamental one is `.then`. + +The syntax is: ```js promise.then( @@ -144,7 +148,7 @@ The second argument of `.then` is a function that: 1. runs when the Promise is rejected, and 2. receives the error. -For instance, here's the reaction to a successfuly resolved promise: +For instance, here's a reaction to a successfuly resolved promise: ```js run let promise = new Promise(function(resolve, reject) { @@ -190,6 +194,8 @@ promise.then(alert); // shows "done!" after 1 second */!* ``` +### catch + If we're interested only in errors, then we can use `null` as the first argument: `.then(null, errorHandlingFunction)`. Or we can use `.catch(errorHandlingFunction)`, which is exactly the same: @@ -206,45 +212,68 @@ promise.catch(alert); // shows "Error: Whoops!" after 1 second The call `.catch(f)` is a complete analog of `.then(null, f)`, it's just a shorthand. -````smart header="On settled promises `then` runs immediately" -If a promise is pending, `.then/catch` handlers wait for the result. Otherwise, if a promise has already settled, they execute immediately: +### finally -```js run -// an immediately resolved promise -let promise = new Promise(resolve => resolve("done!")); +Just like there's a `finally` clause in a regular `try {...} catch {...}`, there's `finally` in promises. -promise.then(alert); // done! (shows up right now) +The call `.finally(f)` is similar to `.then(f, f)` in the sense that it always runs when the promise is settled: be it resolve or reject. + +`finally` is a good handler for performing cleanup, e.g. stopping our loading indicators, as they are not needed any more, no matter what the outcome is. + +Like this: + +```js +new Promise((resolve, reject) => { + /* do something that takes time, and then call resolve/reject */ +}) +*!* + // runs when the promise is settled, doesn't matter successfully or not + .finally(() => stop loading indicator) +*/!* + .then(result => show result, err => show error) ``` -Some tasks may sometimes require time and sometimes finish immediately. The good thing is: the `.then` handler is guaranteed to run in both cases. -```` +It's not exactly an alias though. There are several important differences: -````smart header="Handlers of `.then`/`.catch` are always asynchronous" -Even when the Promise is immediately resolved, code which occurs on lines *below* your `.then`/`.catch` may still execute first. +1. A `finally` handler has no arguments. In `finally` we don't know whether the promise is successful or not. That's all right, as our task is usually to perform "general" finalizing procedures. +2. Finally passes through results and errors to the next handler. -The JavaScript engine has an internal execution queue which gets all `.then/catch` handlers. + For instance, here the result is passed through `finally` to `then`: + ```js run + new Promise((resolve, reject) => { + setTimeout(() => resolve("result"), 2000) + }) + .finally(() => alert("Promise ready")) + .then(result => alert(result)); // <-- .then handles the result + ``` -But it only looks into that queue when the current execution is finished. + And here there's an error in the promise, passed through `finally` to `catch`: -In other words, `.then/catch` handlers are pending execution until the engine is done with the current code. + ```js run + new Promise((resolve, reject) => { + throw new Error("error"); + }) + .finally(() => alert("Promise ready")) + .catch(err => alert(err)); // <-- .catch handles the error object + ``` -For instance, here: + That's very convenient, because finally is not meant to process promise results. So it passes them through. -```js run -// an "immediately" resolved Promise -const executor = resolve => resolve("done!"); -const promise = new Promise(executor); + We'll talk more about promise chaining and result-passing between handlers in the next chapter. -promise.then(alert); // this alert shows last (*) +3. Last, but not least, `.finally(f)` is a more convenient syntax than `.then(f, f)`: no need to duplicate the function. -alert("code finished"); // this alert shows first -``` +````smart header="On settled promises handlers runs immediately" +If a promise is pending, `.then/catch/finally` handlers wait for the result. Otherwise, if a promise has already settled, they execute immediately: -The promise becomes settled immediately, but the engine first finishes the current code, calls `alert`, and only *afterwards* looks into the queue to run `.then` handler. +```js run +// an immediately resolved promise +let promise = new Promise(resolve => resolve("done!")); -So the code *after* `.then` ends up always running *before* the Promise's subscribers, even in the case of an immediately-resolved Promise. +promise.then(alert); // done! (shows up right now) +``` -Usually that's unimportant, but in some scenarios the order may matter a great deal. +The good thing is: a `.then` handler is guaranteed to run whether the promise takes time or settles it immediately. ```` Next, let's see more practical examples of how promises can help us to write asynchronous code. @@ -288,7 +317,7 @@ function loadScript(src) { Usage: ```js run -let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js"); +let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"); promise.then( script => alert(`${script.src} is loaded!`), @@ -300,11 +329,10 @@ promise.then(script => alert('One more handler to do something else!')); We can immediately see a few benefits over the callback-based pattern: -```compare minus="Callbacks" plus="Promises" -- We must have a ready `callback` function when calling `loadScript`. In other words, we must know what to do with the result *before* `loadScript` is called. -- There can be only one callback. -+ Promises allow us to do things in the natural order. First, we run `loadScript`, and `.then` we write what to do with the result. -+ We can call `.then` on a Promise as many times as we want. Each time, we're adding a new "fan", a new subscribing function, to the "subscription list". More about this in the next section: [Promise Chaining](/promise-chaining). -``` -So Promises already give us better code flow and flexibility. But there's more. We'll see that in the next chapters. +| Promises | Callbacks | +|----------|-----------| +| Promises allow us to do things in the natural order. First, we run `loadScript(script)`, and `.then` we write what to do with the result. | We must have a `callback` function at our disposal when calling `loadScript(script, callback)`. In other words, we must know what to do with the result *before* `loadScript` is called. | +| We can call `.then` on a Promise as many times as we want. Each time, we're adding a new "fan", a new subscribing function, to the "subscription list". More about this in the next chapter: [](info:promise-chaining). | There can be only one callback. | + +So Promises give us better code flow and flexibility. But there's more. We'll see that in the next chapters. diff --git a/6-async/02-promise-basics/head.html b/1-js/11-async/02-promise-basics/head.html similarity index 100% rename from 6-async/02-promise-basics/head.html rename to 1-js/11-async/02-promise-basics/head.html diff --git a/6-async/02-promise-basics/promise-init.png b/1-js/11-async/02-promise-basics/promise-init.png similarity index 100% rename from 6-async/02-promise-basics/promise-init.png rename to 1-js/11-async/02-promise-basics/promise-init.png diff --git a/6-async/02-promise-basics/promise-init@2x.png b/1-js/11-async/02-promise-basics/promise-init@2x.png similarity index 100% rename from 6-async/02-promise-basics/promise-init@2x.png rename to 1-js/11-async/02-promise-basics/promise-init@2x.png diff --git a/1-js/11-async/02-promise-basics/promise-reject-1.png b/1-js/11-async/02-promise-basics/promise-reject-1.png new file mode 100644 index 000000000..3ae74879c Binary files /dev/null and b/1-js/11-async/02-promise-basics/promise-reject-1.png differ diff --git a/1-js/11-async/02-promise-basics/promise-reject-1@2x.png b/1-js/11-async/02-promise-basics/promise-reject-1@2x.png new file mode 100644 index 000000000..9eff3793c Binary files /dev/null and b/1-js/11-async/02-promise-basics/promise-reject-1@2x.png differ diff --git a/1-js/11-async/02-promise-basics/promise-resolve-1.png b/1-js/11-async/02-promise-basics/promise-resolve-1.png new file mode 100644 index 000000000..b4bb51826 Binary files /dev/null and b/1-js/11-async/02-promise-basics/promise-resolve-1.png differ diff --git a/1-js/11-async/02-promise-basics/promise-resolve-1@2x.png b/1-js/11-async/02-promise-basics/promise-resolve-1@2x.png new file mode 100644 index 000000000..ecb4af35d Binary files /dev/null and b/1-js/11-async/02-promise-basics/promise-resolve-1@2x.png differ diff --git a/1-js/11-async/02-promise-basics/promise-resolve-reject.png b/1-js/11-async/02-promise-basics/promise-resolve-reject.png new file mode 100644 index 000000000..6f0294f01 Binary files /dev/null and b/1-js/11-async/02-promise-basics/promise-resolve-reject.png differ diff --git a/1-js/11-async/02-promise-basics/promise-resolve-reject@2x.png b/1-js/11-async/02-promise-basics/promise-resolve-reject@2x.png new file mode 100644 index 000000000..b59301ff7 Binary files /dev/null and b/1-js/11-async/02-promise-basics/promise-resolve-reject@2x.png differ diff --git a/6-async/03-promise-chaining/01-then-vs-catch/solution.md b/1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md similarity index 100% rename from 6-async/03-promise-chaining/01-then-vs-catch/solution.md rename to 1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md diff --git a/6-async/03-promise-chaining/01-then-vs-catch/task.md b/1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md similarity index 96% rename from 6-async/03-promise-chaining/01-then-vs-catch/task.md rename to 1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md index 51c08d7c9..cefca60aa 100644 --- a/6-async/03-promise-chaining/01-then-vs-catch/task.md +++ b/1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md @@ -3,10 +3,11 @@ Are these code fragments equal? In other words, do they behave the same way in any circumstances, for any handler functions? ```js -promise.then(f1, f2); +promise.then(f1).catch(f2); ``` -Versus; +Versus: + ```js -promise.then(f1).catch(f2); +promise.then(f1, f2); ``` diff --git a/1-js/11-async/03-promise-chaining/article.md b/1-js/11-async/03-promise-chaining/article.md new file mode 100644 index 000000000..62e05bdd6 --- /dev/null +++ b/1-js/11-async/03-promise-chaining/article.md @@ -0,0 +1,387 @@ + +# Promises chaining + +Let's return to the problem mentioned in the chapter : we have a sequence of asynchronous tasks to be done one after another. For instance, loading scripts. How can we code it well? + +Promises provide a couple of recipes to do that. + +In this chapter we cover promise chaining. + +It looks like this: + +```js run +new Promise(function(resolve, reject) { + + setTimeout(() => resolve(1), 1000); // (*) + +}).then(function(result) { // (**) + + alert(result); // 1 + return result * 2; + +}).then(function(result) { // (***) + + alert(result); // 2 + return result * 2; + +}).then(function(result) { + + alert(result); // 4 + return result * 2; + +}); +``` + +The idea is that the result is passed through the chain of `.then` handlers. + +Here the flow is: +1. The initial promise resolves in 1 second `(*)`, +2. Then the `.then` handler is called `(**)`. +3. The value that it returns is passed to the next `.then` handler `(***)` +4. ...and so on. + +As the result is passed along the chain of handlers, we can see a sequence of `alert` calls: `1` -> `2` -> `4`. + +![](promise-then-chain.png) + +The whole thing works, because a call to `promise.then` returns a promise, so that we can call the next `.then` on it. + +When a handler returns a value, it becomes the result of that promise, so the next `.then` is called with it. + +To make these words more clear, here's the start of the chain: + +```js run +new Promise(function(resolve, reject) { + + setTimeout(() => resolve(1), 1000); + +}).then(function(result) { + + alert(result); + return result * 2; // <-- (1) + +}) // <-- (2) +// .then… +``` + +The value returned by `.then` is a promise, that's why we are able to add another `.then` at `(2)`. When the value is returned in `(1)`, that promise becomes resolved, so the next handler runs with the value. + +**A classic newbie error: technically we can also add many `.then` to a single promise. This is not chaining.** + +For example: +```js run +let promise = new Promise(function(resolve, reject) { + setTimeout(() => resolve(1), 1000); +}); + +promise.then(function(result) { + alert(result); // 1 + return result * 2; +}); + +promise.then(function(result) { + alert(result); // 1 + return result * 2; +}); + +promise.then(function(result) { + alert(result); // 1 + return result * 2; +}); +``` + +What we did here is just several handlers to one promise. They don't pass the result to each other, instead they process it independently. + +Here's the picture (compare it with the chaining above): + +![](promise-then-many.png) + +All `.then` on the same promise get the same result -- the result of that promise. So in the code above all `alert` show the same: `1`. + +In practice we rarely need multiple handlers for one promise. Chaining is used much more often. + +## Returning promises + +Normally, a value returned by a `.then` handler is immediately passed to the next handler. But there's an exception. + +If the returned value is a promise, then the further execution is suspended until it settles. After that, the result of that promise is given to the next `.then` handler. + +For instance: + +```js run +new Promise(function(resolve, reject) { + + setTimeout(() => resolve(1), 1000); + +}).then(function(result) { + + alert(result); // 1 + +*!* + return new Promise((resolve, reject) => { // (*) + setTimeout(() => resolve(result * 2), 1000); + }); +*/!* + +}).then(function(result) { // (**) + + alert(result); // 2 + + return new Promise((resolve, reject) => { + setTimeout(() => resolve(result * 2), 1000); + }); + +}).then(function(result) { + + alert(result); // 4 + +}); +``` + +Here the first `.then` shows `1` returns `new Promise(…)` in the line `(*)`. After one second it resolves, and the result (the argument of `resolve`, here it's `result*2`) is passed on to handler of the second `.then` in the line `(**)`. It shows `2` and does the same thing. + +So the output is again 1 -> 2 -> 4, but now with 1 second delay between `alert` calls. + +Returning promises allows us to build chains of asynchronous actions. + +## Example: loadScript + +Let's use this feature with `loadScript` to load scripts one by one, in sequence: + +```js run +loadScript("/article/promise-chaining/one.js") + .then(function(script) { + return loadScript("/article/promise-chaining/two.js"); + }) + .then(function(script) { + return loadScript("/article/promise-chaining/three.js"); + }) + .then(function(script) { + // use functions declared in scripts + // to show that they indeed loaded + one(); + two(); + three(); + }); +``` + +This code can be made bit shorter with arrow functions: + +```js run +loadScript("/article/promise-chaining/one.js") + .then(script => loadScript("/article/promise-chaining/two.js")) + .then(script => loadScript("/article/promise-chaining/three.js")) + .then(script => { + // scripts are loaded, we can use functions declared there + one(); + two(); + three(); + }); +``` + + +Here each `loadScript` call returns a promise, and the next `.then` runs when it resolves. Then it initiates the loading of the next script. So scripts are loaded one after another. + +We can add more asynchronous actions to the chain. Please note that code is still "flat", it grows down, not to the right. There are no signs of "pyramid of doom". + +Please note that technically we can add `.then` directly to each `loadScript`, like this: + +```js run +loadScript("/article/promise-chaining/one.js").then(script1 => { + loadScript("/article/promise-chaining/two.js").then(script2 => { + loadScript("/article/promise-chaining/three.js").then(script3 => { + // this function has access to variables script1, script2 and script3 + one(); + two(); + three(); + }); + }); +}); +``` + +This code does the same: loads 3 scripts in sequence. But it "grows to the right". So we have the same problem as with callbacks. + +People who start to use promises sometimes don't know about chaining, so they write it this way. Generally, chaining is preferred. + +Sometimes it's ok to write `.then` directly, because the nested function has access to the outer scope. In the example above the most nested callback has access to all variables `script1`, `script2`, `script3`. But that's an exception rather than a rule. + + +````smart header="Thenables" +To be precise, `.then` may return an arbitrary "thenable" object, and it will be treated the same way as a promise. + +A "thenable" object is any object with a method `.then`. + +The idea is that 3rd-party libraries may implement "promise-compatible" objects of their own. They can have extended set of methods, but also be compatible with native promises, because they implement `.then`. + +Here's an example of a thenable object: + +```js run +class Thenable { + constructor(num) { + this.num = num; + } + then(resolve, reject) { + alert(resolve); // function() { native code } + // resolve with this.num*2 after the 1 second + setTimeout(() => resolve(this.num * 2), 1000); // (**) + } +} + +new Promise(resolve => resolve(1)) + .then(result => { + return new Thenable(result); // (*) + }) + .then(alert); // shows 2 after 1000ms +``` + +JavaScript checks the object returned by `.then` handler in the line `(*)`: if it has a callable method named `then`, then it calls that method providing native functions `resolve`, `reject` as arguments (similar to executor) and waits until one of them is called. In the example above `resolve(2)` is called after 1 second `(**)`. Then the result is passed further down the chain. + +This feature allows to integrate custom objects with promise chains without having to inherit from `Promise`. +```` + + +## Bigger example: fetch + +In frontend programming promises are often used for network requests. So let's see an extended example of that. + +We'll use the [fetch](mdn:api/WindowOrWorkerGlobalScope/fetch) method to load the information about the user from the remote server. The method is quite complex, it has many optional parameters, but the basic usage is quite simple: + +```js +let promise = fetch(url); +``` + +This makes a network request to the `url` and returns a promise. The promise resolves with a `response` object when the remote server responds with headers, but *before the full response is downloaded*. + +To read the full response, we should call a method `response.text()`: it returns a promise that resolves when the full text downloaded from the remote server, with that text as a result. + +The code below makes a request to `user.json` and loads its text from the server: + +```js run +fetch('/article/promise-chaining/user.json') + // .then below runs when the remote server responds + .then(function(response) { + // response.text() returns a new promise that resolves with the full response text + // when we finish downloading it + return response.text(); + }) + .then(function(text) { + // ...and here's the content of the remote file + alert(text); // {"name": "iliakan", isAdmin: true} + }); +``` + +There is also a method `response.json()` that reads the remote data and parses it as JSON. In our case that's even more convenient, so let's switch to it. + +We'll also use arrow functions for brevity: + +```js run +// same as above, but response.json() parses the remote content as JSON +fetch('/article/promise-chaining/user.json') + .then(response => response.json()) + .then(user => alert(user.name)); // iliakan +``` + +Now let's do something with the loaded user. + +For instance, we can make one more request to github, load the user profile and show the avatar: + +```js run +// Make a request for user.json +fetch('/article/promise-chaining/user.json') + // Load it as json + .then(response => response.json()) + // Make a request to github + .then(user => fetch(`https://api.github.com/users/${user.name}`)) + // Load the response as json + .then(response => response.json()) + // Show the avatar image (githubUser.avatar_url) for 3 seconds (maybe animate it) + .then(githubUser => { + let img = document.createElement('img'); + img.src = githubUser.avatar_url; + img.className = "promise-avatar-example"; + document.body.append(img); + + setTimeout(() => img.remove(), 3000); // (*) + }); +``` + +The code works, see comments about the details, but it should be quite self-descriptive. Although, there's a potential problem in it, a typical error of those who begin to use promises. + +Look at the line `(*)`: how can we do something *after* the avatar has finished showing and gets removed? For instance, we'd like to show a form for editing that user or something else. As of now, there's no way. + +To make the chain extendable, we need to return a promise that resolves when the avatar finishes showing. + +Like this: + +```js run +fetch('/article/promise-chaining/user.json') + .then(response => response.json()) + .then(user => fetch(`https://api.github.com/users/${user.name}`)) + .then(response => response.json()) +*!* + .then(githubUser => new Promise(function(resolve, reject) { +*/!* + let img = document.createElement('img'); + img.src = githubUser.avatar_url; + img.className = "promise-avatar-example"; + document.body.append(img); + + setTimeout(() => { + img.remove(); +*!* + resolve(githubUser); +*/!* + }, 3000); + })) + // triggers after 3 seconds + .then(githubUser => alert(`Finished showing ${githubUser.name}`)); +``` + +Now right after `setTimeout` runs `img.remove()`, it calls `resolve(githubUser)`, thus passing the control to the next `.then` in the chain and passing forward the user data. + +As a rule, an asynchronous action should always return a promise. + +That makes it possible to plan actions after it. Even if we don't plan to extend the chain now, we may need it later. + +Finally, we can split the code into reusable functions: + +```js run +function loadJson(url) { + return fetch(url) + .then(response => response.json()); +} + +function loadGithubUser(name) { + return fetch(`https://api.github.com/users/${name}`) + .then(response => response.json()); +} + +function showAvatar(githubUser) { + return new Promise(function(resolve, reject) { + let img = document.createElement('img'); + img.src = githubUser.avatar_url; + img.className = "promise-avatar-example"; + document.body.append(img); + + setTimeout(() => { + img.remove(); + resolve(githubUser); + }, 3000); + }); +} + +// Use them: +loadJson('/article/promise-chaining/user.json') + .then(user => loadGithubUser(user.name)) + .then(showAvatar) + .then(githubUser => alert(`Finished showing ${githubUser.name}`)); + // ... +``` + +## Summary + +If a `.then` (or `catch/finally`, doesn't matter) handler returns a promise, the rest of the chain waits until it settles. When it does, its result (or error) is passed further. + +Here's a full picture: + +![](promise-handler-variants.png) diff --git a/6-async/03-promise-chaining/getMessage.js b/1-js/11-async/03-promise-chaining/getMessage.js similarity index 100% rename from 6-async/03-promise-chaining/getMessage.js rename to 1-js/11-async/03-promise-chaining/getMessage.js diff --git a/6-async/03-promise-chaining/head.html b/1-js/11-async/03-promise-chaining/head.html similarity index 100% rename from 6-async/03-promise-chaining/head.html rename to 1-js/11-async/03-promise-chaining/head.html diff --git a/6-async/03-promise-chaining/one.js b/1-js/11-async/03-promise-chaining/one.js similarity index 100% rename from 6-async/03-promise-chaining/one.js rename to 1-js/11-async/03-promise-chaining/one.js diff --git a/1-js/11-async/03-promise-chaining/promise-handler-variants-2.png b/1-js/11-async/03-promise-chaining/promise-handler-variants-2.png new file mode 100644 index 000000000..712f57b82 Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-handler-variants-2.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-handler-variants-2@2x.png b/1-js/11-async/03-promise-chaining/promise-handler-variants-2@2x.png new file mode 100644 index 000000000..5b57be809 Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-handler-variants-2@2x.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-handler-variants.png b/1-js/11-async/03-promise-chaining/promise-handler-variants.png new file mode 100644 index 000000000..2643fde89 Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-handler-variants.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-handler-variants@2x.png b/1-js/11-async/03-promise-chaining/promise-handler-variants@2x.png new file mode 100644 index 000000000..313e241cc Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-handler-variants@2x.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-then-chain.png b/1-js/11-async/03-promise-chaining/promise-then-chain.png new file mode 100644 index 000000000..fab8a92ad Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-then-chain.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-then-chain@2x.png b/1-js/11-async/03-promise-chaining/promise-then-chain@2x.png new file mode 100644 index 000000000..421b59359 Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-then-chain@2x.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-then-many.png b/1-js/11-async/03-promise-chaining/promise-then-many.png new file mode 100644 index 000000000..b13d20a9e Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-then-many.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-then-many@2x.png b/1-js/11-async/03-promise-chaining/promise-then-many@2x.png new file mode 100644 index 000000000..f0609c946 Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-then-many@2x.png differ diff --git a/6-async/03-promise-chaining/three.js b/1-js/11-async/03-promise-chaining/three.js similarity index 100% rename from 6-async/03-promise-chaining/three.js rename to 1-js/11-async/03-promise-chaining/three.js diff --git a/6-async/03-promise-chaining/two.js b/1-js/11-async/03-promise-chaining/two.js similarity index 100% rename from 6-async/03-promise-chaining/two.js rename to 1-js/11-async/03-promise-chaining/two.js diff --git a/6-async/03-promise-chaining/user.json b/1-js/11-async/03-promise-chaining/user.json similarity index 100% rename from 6-async/03-promise-chaining/user.json rename to 1-js/11-async/03-promise-chaining/user.json diff --git a/6-async/03-promise-chaining/02-error-async/solution.md b/1-js/11-async/04-promise-error-handling/01-error-async/solution.md similarity index 100% rename from 6-async/03-promise-chaining/02-error-async/solution.md rename to 1-js/11-async/04-promise-error-handling/01-error-async/solution.md diff --git a/6-async/03-promise-chaining/02-error-async/task.md b/1-js/11-async/04-promise-error-handling/01-error-async/task.md similarity index 100% rename from 6-async/03-promise-chaining/02-error-async/task.md rename to 1-js/11-async/04-promise-error-handling/01-error-async/task.md diff --git a/1-js/11-async/04-promise-error-handling/article.md b/1-js/11-async/04-promise-error-handling/article.md new file mode 100644 index 000000000..ca67202aa --- /dev/null +++ b/1-js/11-async/04-promise-error-handling/article.md @@ -0,0 +1,343 @@ + +# Error handling with promises + +Asynchronous actions may sometimes fail: in case of an error the corresponding promise becomes rejected. For instance, `fetch` fails if the remote server is not available. We can use `.catch` to handle errors (rejections). + +Promise chaining is great at that aspect. When a promise rejects, the control jumps to the closest rejection handler down the chain. That's very convenient in practice. + +For instance, in the code below the URL is wrong (no such server) and `.catch` handles the error: + +```js run +*!* +fetch('https://no-such-server.blabla') // rejects +*/!* + .then(response => response.json()) + .catch(err => alert(err)) // TypeError: failed to fetch (the text may vary) +``` + +Or, maybe, everything is all right with the server, but the response is not valid JSON: + +```js run +fetch('/') // fetch works fine now, the server responds successfully +*!* + .then(response => response.json()) // rejects: the page is HTML, not a valid json +*/!* + .catch(err => alert(err)) // SyntaxError: Unexpected token < in JSON at position 0 +``` + +The easiest way to catch all errors is to append `.catch` to the end of chain: + +```js run +fetch('/article/promise-chaining/user.json') + .then(response => response.json()) + .then(user => fetch(`https://api.github.com/users/${user.name}`)) + .then(response => response.json()) + .then(githubUser => new Promise((resolve, reject) => { + let img = document.createElement('img'); + img.src = githubUser.avatar_url; + img.className = "promise-avatar-example"; + document.body.append(img); + + setTimeout(() => { + img.remove(); + resolve(githubUser); + }, 3000); + })) +*!* + .catch(error => alert(error.message)); +*/!* +``` + +Normally, `.catch` doesn't trigger at all, because there are no errors. But if any of the promises above rejects (a network problem or invalid json or whatever), then it would catch it. + +## Implicit try..catch + +The code of a promise executor and promise handlers has an "invisible `try..catch`" around it. If an error happens, it gets caught and treated as a rejection. + +For instance, this code: + +```js run +new Promise((resolve, reject) => { +*!* + throw new Error("Whoops!"); +*/!* +}).catch(alert); // Error: Whoops! +``` + +...Works exactly the same as this: + +```js run +new Promise((resolve, reject) => { +*!* + reject(new Error("Whoops!")); +*/!* +}).catch(alert); // Error: Whoops! +``` + +The "invisible `try..catch`" around the executor automatically catches the error and treats it as a rejection. + +This happens not only in the executor, but in its handlers as well. If we `throw` inside a `.then` handler, that means a rejected promise, so the control jumps to the nearest error handler. + +Here's an example: + +```js run +new Promise((resolve, reject) => { + resolve("ok"); +}).then((result) => { +*!* + throw new Error("Whoops!"); // rejects the promise +*/!* +}).catch(alert); // Error: Whoops! +``` + +This happens for all errors, not just those caused by the `throw` statement. For example, a programming error: + +```js run +new Promise((resolve, reject) => { + resolve("ok"); +}).then((result) => { +*!* + blabla(); // no such function +*/!* +}).catch(alert); // ReferenceError: blabla is not defined +``` + +The final `.catch` not only catches explicit rejections, but also occasional errors in the handlers above. + +## Rethrowing + +As we already noticed, `.catch` behaves like `try..catch`. We may have as many `.then` handlers as we want, and then use a single `.catch` at the end to handle errors in all of them. + +In a regular `try..catch` we can analyze the error and maybe rethrow it if can't handle. The same thing is possible for promises. + +If we `throw` inside `.catch`, then the control goes to the next closest error handler. And if we handle the error and finish normally, then it continues to the closest successful `.then` handler. + +In the example below the `.catch` successfully handles the error: + +```js run +// the execution: catch -> then +new Promise((resolve, reject) => { + + throw new Error("Whoops!"); + +}).catch(function(error) { + + alert("The error is handled, continue normally"); + +}).then(() => alert("Next successful handler runs")); +``` + +Here the `.catch` block finishes normally. So the next successful `.then` handler is called. + +In the example below we see the other situation with `.catch`. The handler `(*)` catches the error and just can't handle it (e.g. it only knows how to handle `URIError`), so it throws it again: + +```js run +// the execution: catch -> catch -> then +new Promise((resolve, reject) => { + + throw new Error("Whoops!"); + +}).catch(function(error) { // (*) + + if (error instanceof URIError) { + // handle it + } else { + alert("Can't handle such error"); + +*!* + throw error; // throwing this or another error jumps to the next catch +*/!* + } + +}).then(function() { + /* never runs here */ +}).catch(error => { // (**) + + alert(`The unknown error has occurred: ${error}`); + // don't return anything => execution goes the normal way + +}); +``` + +Then the execution jumps from the first `.catch` `(*)` to the next one `(**)` down the chain. + +In the section below we'll see a practical example of rethrowing. + +## Fetch error handling example + +Let's improve error handling for the user-loading example. + +The promise returned by [fetch](mdn:api/WindowOrWorkerGlobalScope/fetch) rejects when it's impossible to make a request. For instance, a remote server is not available, or the URL is malformed. But if the remote server responds with error 404, or even error 500, then it's considered a valid response. + +What if the server returns a non-JSON page with error 500 in the line `(*)`? What if there's no such user, and github returns a page with error 404 at `(**)`? + +```js run +fetch('no-such-user.json') // (*) + .then(response => response.json()) + .then(user => fetch(`https://api.github.com/users/${user.name}`)) // (**) + .then(response => response.json()) + .catch(alert); // SyntaxError: Unexpected token < in JSON at position 0 + // ... +``` + + +As of now, the code tries to load the response as JSON no matter what and dies with a syntax error. You can see that by running the example above, as the file `no-such-user.json` doesn't exist. + +That's not good, because the error just falls through the chain, without details: what failed and where. + +So let's add one more step: we should check the `response.status` property that has HTTP status, and if it's not 200, then throw an error. + +```js run +class HttpError extends Error { // (1) + constructor(response) { + super(`${response.status} for ${response.url}`); + this.name = 'HttpError'; + this.response = response; + } +} + +function loadJson(url) { // (2) + return fetch(url) + .then(response => { + if (response.status == 200) { + return response.json(); + } else { + throw new HttpError(response); + } + }) +} + +loadJson('no-such-user.json') // (3) + .catch(alert); // HttpError: 404 for .../no-such-user.json +``` + +1. We make a custom class for HTTP Errors to distinguish them from other types of errors. Besides, the new class has a constructor that accepts `response` object and saves it in the error. So error-handling code will be able to access it. +2. Then we put together the requesting and error-handling code into a function that fetches the `url` *and* treats any non-200 status as an error. That's convenient, because we often need such logic. +3. Now `alert` shows a more helpful descriptive message. + +The great thing about having our own class for errors is that we can easily check for it in error-handling code. + +For instance, we can make a request, and then if we get 404 -- ask the user to modify the information. + +The code below loads a user with the given name from github. If there's no such user, then it asks for the correct name: + +```js run +function demoGithubUser() { + let name = prompt("Enter a name?", "iliakan"); + + return loadJson(`https://api.github.com/users/${name}`) + .then(user => { + alert(`Full name: ${user.name}.`); + return user; + }) + .catch(err => { +*!* + if (err instanceof HttpError && err.response.status == 404) { +*/!* + alert("No such user, please reenter."); + return demoGithubUser(); + } else { + throw err; // (*) + } + }); +} + +demoGithubUser(); +``` + +Please note: `.catch` here catches all errors, but it "knows how to handle" only `HttpError 404`. In that particular case it means that there's no such user, and `.catch` just retries in that case. + +For other errors, it has no idea what could go wrong. Maybe a programming error or something. So it just rethrows it in the line `(*)`. + +## Unhandled rejections + +What happens when an error is not handled? For instance, after the rethrow `(*)` in the example above. + +Or we could just forget to append an error handler to the end of the chain, like here: + +```js untrusted run refresh +new Promise(function() { + noSuchFunction(); // Error here (no such function) +}) + .then(() => { + // zero or many promise handlers + }); // without .catch at the end! +``` + +In case of an error, the promise state becomes "rejected", and the execution should jump to the closest rejection handler. But there is no such handler in the examples above. So the error gets "stuck". + +In practice, just like with a regular unhandled errors, it means that something has terribly gone wrong, the script probably died. + +Most JavaScript engines track such situations and generate a global error in that case. We can see it in the console. + +In the browser we can catch such errors using the event `unhandledrejection`: + +```js run +*!* +window.addEventListener('unhandledrejection', function(event) { + // the event object has two special properties: + alert(event.promise); // [object Promise] - the promise that generated the error + alert(event.reason); // Error: Whoops! - the unhandled error object +}); +*/!* + +new Promise(function() { + throw new Error("Whoops!"); +}); // no catch to handle the error +``` + +The event is the part of the [HTML standard](https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections). + +If an error occurs, and there's no `.catch`, the `unhandledrejection` handler triggers, and gets the `event` object with the information about the error, so we can do something. + +Usually such errors are unrecoverable, so our best way out is to inform the user about the problem and probably report the incident to the server. + +In non-browser environments like Node.js there are other similar ways to track unhandled errors. + + +## Summary + +- `.catch` handles promise rejections of all kinds: be it a `reject()` call, or an error thrown in a handler. +- We should place `.catch` exactly in places where we want to handle errors and know how to handle them. The handler should analyze errors (custom error classes help) and rethrow unknown ones. +- It's normal not to use `.catch` if we don't know how to handle errors (all errors are unrecoverable). +- In any case we should have the `unhandledrejection` event handler (for browsers, and analogs for other environments), to track unhandled errors and inform the user (and probably our server) about the them, so that our app never "just dies". + +And finally, if we have load-indication, then `.finally` is a great handler to stop it when the fetch is complete: + +```js run +function demoGithubUser() { + let name = prompt("Enter a name?", "iliakan"); + +*!* + document.body.style.opacity = 0.3; // (1) start the indication +*/!* + + return loadJson(`https://api.github.com/users/${name}`) +*!* + .finally(() => { // (2) stop the indication + document.body.style.opacity = ''; + return new Promise(resolve => setTimeout(resolve, 0)); // (*) + }) +*/!* + .then(user => { + alert(`Full name: ${user.name}.`); + return user; + }) + .catch(err => { + if (err instanceof HttpError && err.response.status == 404) { + alert("No such user, please reenter."); + return demoGithubUser(); + } else { + throw err; + } + }); +} + +demoGithubUser(); +``` + +Here on the line `(1)` we indicate loading by dimming the document. The method doesn't matter, could use any type of indication instead. + +When the promise is settled, be it a successful fetch or an error, `finally` triggers at the line `(2)` and stops the indication. + +There's a little browser trick `(*)` with returning a zero-timeout promise from `finally`. That's because some browsers (like Chrome) need "a bit time" outside promise handlers to paint document changes. So it ensures that the indication is visually stopped before going further on the chain. diff --git a/1-js/11-async/04-promise-error-handling/getMessage.js b/1-js/11-async/04-promise-error-handling/getMessage.js new file mode 100644 index 000000000..6c5893433 --- /dev/null +++ b/1-js/11-async/04-promise-error-handling/getMessage.js @@ -0,0 +1,3 @@ +function getMessage() { + return "Hello, world!"; +} diff --git a/1-js/11-async/04-promise-error-handling/head.html b/1-js/11-async/04-promise-error-handling/head.html new file mode 100644 index 000000000..31c6b4271 --- /dev/null +++ b/1-js/11-async/04-promise-error-handling/head.html @@ -0,0 +1,41 @@ + + + diff --git a/6-async/04-promise-api/one.js b/1-js/11-async/04-promise-error-handling/one.js similarity index 100% rename from 6-async/04-promise-api/one.js rename to 1-js/11-async/04-promise-error-handling/one.js diff --git a/1-js/11-async/04-promise-error-handling/promise-handler-variants-2.png b/1-js/11-async/04-promise-error-handling/promise-handler-variants-2.png new file mode 100644 index 000000000..712f57b82 Binary files /dev/null and b/1-js/11-async/04-promise-error-handling/promise-handler-variants-2.png differ diff --git a/1-js/11-async/04-promise-error-handling/promise-handler-variants-2@2x.png b/1-js/11-async/04-promise-error-handling/promise-handler-variants-2@2x.png new file mode 100644 index 000000000..5b57be809 Binary files /dev/null and b/1-js/11-async/04-promise-error-handling/promise-handler-variants-2@2x.png differ diff --git a/1-js/11-async/04-promise-error-handling/promise-handler-variants.png b/1-js/11-async/04-promise-error-handling/promise-handler-variants.png new file mode 100644 index 000000000..2643fde89 Binary files /dev/null and b/1-js/11-async/04-promise-error-handling/promise-handler-variants.png differ diff --git a/1-js/11-async/04-promise-error-handling/promise-handler-variants@2x.png b/1-js/11-async/04-promise-error-handling/promise-handler-variants@2x.png new file mode 100644 index 000000000..313e241cc Binary files /dev/null and b/1-js/11-async/04-promise-error-handling/promise-handler-variants@2x.png differ diff --git a/1-js/11-async/04-promise-error-handling/promise-then-chain.png b/1-js/11-async/04-promise-error-handling/promise-then-chain.png new file mode 100644 index 000000000..fab8a92ad Binary files /dev/null and b/1-js/11-async/04-promise-error-handling/promise-then-chain.png differ diff --git a/1-js/11-async/04-promise-error-handling/promise-then-chain@2x.png b/1-js/11-async/04-promise-error-handling/promise-then-chain@2x.png new file mode 100644 index 000000000..421b59359 Binary files /dev/null and b/1-js/11-async/04-promise-error-handling/promise-then-chain@2x.png differ diff --git a/1-js/11-async/04-promise-error-handling/promise-then-many.png b/1-js/11-async/04-promise-error-handling/promise-then-many.png new file mode 100644 index 000000000..b13d20a9e Binary files /dev/null and b/1-js/11-async/04-promise-error-handling/promise-then-many.png differ diff --git a/1-js/11-async/04-promise-error-handling/promise-then-many@2x.png b/1-js/11-async/04-promise-error-handling/promise-then-many@2x.png new file mode 100644 index 000000000..f0609c946 Binary files /dev/null and b/1-js/11-async/04-promise-error-handling/promise-then-many@2x.png differ diff --git a/1-js/11-async/04-promise-error-handling/three.js b/1-js/11-async/04-promise-error-handling/three.js new file mode 100644 index 000000000..8536e85a1 --- /dev/null +++ b/1-js/11-async/04-promise-error-handling/three.js @@ -0,0 +1,3 @@ +function three() { + alert(3); +} diff --git a/6-async/04-promise-api/two.js b/1-js/11-async/04-promise-error-handling/two.js similarity index 100% rename from 6-async/04-promise-api/two.js rename to 1-js/11-async/04-promise-error-handling/two.js diff --git a/6-async/04-promise-api/iliakan.json b/1-js/11-async/04-promise-error-handling/user.json similarity index 100% rename from 6-async/04-promise-api/iliakan.json rename to 1-js/11-async/04-promise-error-handling/user.json diff --git a/6-async/04-promise-api/01-promise-errors-as-results/solution.md b/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.md similarity index 100% rename from 6-async/04-promise-api/01-promise-errors-as-results/solution.md rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.md diff --git a/6-async/04-promise-api/01-promise-errors-as-results/solution.view/index.html b/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html similarity index 100% rename from 6-async/04-promise-api/01-promise-errors-as-results/solution.view/index.html rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html diff --git a/6-async/04-promise-api/01-promise-errors-as-results/source.view/index.html b/1-js/11-async/05-promise-api/01-promise-errors-as-results/source.view/index.html similarity index 100% rename from 6-async/04-promise-api/01-promise-errors-as-results/source.view/index.html rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/source.view/index.html diff --git a/6-async/04-promise-api/01-promise-errors-as-results/task.md b/1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md similarity index 90% rename from 6-async/04-promise-api/01-promise-errors-as-results/task.md rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md index d6904f1bd..c37ad56e3 100644 --- a/6-async/04-promise-api/01-promise-errors-as-results/task.md +++ b/1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md @@ -20,13 +20,13 @@ Promise.all(urls.map(url => fetch(url))) }); ``` -The problem is that if any of requests fails, then `Promise.all` rejects with the error, and we loose results of all the other requests. +The problem is that if any of requests fails, then `Promise.all` rejects with the error, and we lose the results of all the other requests. That's not good. Modify the code so that the array `responses` in the line `(*)` would include the response objects for successful fetches and error objects for failed ones. -For instance, if one of URLs is bad, then it should be like: +For instance, if one of the URLs is bad, then it should be like: ```js let urls = [ diff --git a/6-async/01-callbacks/01-animate-circle-callback/solution.md b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.md similarity index 100% rename from 6-async/01-callbacks/01-animate-circle-callback/solution.md rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.md diff --git a/6-async/04-promise-api/02-promise-errors-as-results-2/solution.view/index.html b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html similarity index 100% rename from 6-async/04-promise-api/02-promise-errors-as-results-2/solution.view/index.html rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html diff --git a/6-async/04-promise-api/02-promise-errors-as-results-2/source.view/index.html b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html similarity index 100% rename from 6-async/04-promise-api/02-promise-errors-as-results-2/source.view/index.html rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html diff --git a/6-async/04-promise-api/02-promise-errors-as-results-2/task.md b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md similarity index 85% rename from 6-async/04-promise-api/02-promise-errors-as-results-2/task.md rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md index ec28728b0..d28cf87e7 100644 --- a/6-async/04-promise-api/02-promise-errors-as-results-2/task.md +++ b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md @@ -1,6 +1,6 @@ # Fault-tolerant fetch with JSON -Improve the solution of the previous task . Now we need not just to call `fetch`, but to load the JSON objects from given URLs. +Improve the solution of the previous task . Now we need not just to call `fetch`, but to load the JSON objects from the given URLs. Here's the example code to do that: @@ -25,7 +25,7 @@ Promise.all(urls.map(url => fetch(url))) }); ``` -The problem is that if any of requests fails, then `Promise.all` rejects with the error, and we loose results of all the other requests. So the code above is not fault-tolerant, just like the one in the previous task. +The problem is that if any of requests fails, then `Promise.all` rejects with the error, and we lose results of all the other requests. So the code above is not fault-tolerant, just like the one in the previous task. Modify the code so that the array in the line `(*)` would include parsed JSON for successful requests and error for errored ones. diff --git a/6-async/04-promise-api/article.md b/1-js/11-async/05-promise-api/article.md similarity index 80% rename from 6-async/04-promise-api/article.md rename to 1-js/11-async/05-promise-api/article.md index e0e5eda47..d049f3b09 100644 --- a/6-async/04-promise-api/article.md +++ b/1-js/11-async/05-promise-api/article.md @@ -35,13 +35,13 @@ function loadCached(url) { return fetch(url) .then(response => response.text()) .then(text => { - cache.set(url,text); + cache.set(url,text); return text; }); } ``` -We can use `loadCached(url).then(…)`, because the function is guaranteed to return a promise. That's the purpose `Promise.resolve` in the line `(*)`: it makes sure the interface unified. We can always use `.then` after `loadCached`. +We can use `loadCached(url).then(…)`, because the function is guaranteed to return a promise. That's the purpose `Promise.resolve` serves in the line `(*)`: it makes sure the interface is unified. We can always use `.then` after `loadCached`. ## Promise.reject @@ -63,23 +63,29 @@ We cover it here for completeness, rarely used in real code. ## Promise.all -The method to run many promises in parallel and wait till all of them are ready. +Let's say we want to run many promises to execute in parallel, and wait till all of them are ready. + +For instance, download several URLs in parallel and process the content when all are done. + +That's what `Promise.all` is for. The syntax is: ```js -let promise = Promise.all(iterable); +let promise = Promise.all([...promises...]); ``` -It takes an `iterable` object with promises, technically it can be any iterable, but usually it's an array, and returns a new promise. The new promise resolves with when all of them are settled and has an array of their results. +It takes an array of promises (technically can be any iterable, but usually an array) and returns a new promise. + +The new promise resolves when all listed promises are settled and has an array of their results. For instance, the `Promise.all` below settles after 3 seconds, and then its result is an array `[1, 2, 3]`: ```js run Promise.all([ - new Promise((resolve, reject) => setTimeout(() => resolve(1), 3000)), // 1 - new Promise((resolve, reject) => setTimeout(() => resolve(2), 2000)), // 2 - new Promise((resolve, reject) => setTimeout(() => resolve(3), 1000)) // 3 + new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1 + new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2 + new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3 ]).then(alert); // 1,2,3 when promises are ready: each promise contributes an array member ``` @@ -132,7 +138,6 @@ If any of the promises is rejected, `Promise.all` immediately rejects with that For instance: - ```js run Promise.all([ new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)), @@ -145,12 +150,12 @@ Promise.all([ Here the second promise rejects in two seconds. That leads to immediate rejection of `Promise.all`, so `.catch` executes: the rejection error becomes the outcome of the whole `Promise.all`. -The important detail is that promises provide no way to "cancel" or "abort" their execution. So other promises continue to execute, and the eventually settle, but all their results are ignored. +The important detail is that promises provide no way to "cancel" or "abort" their execution. So other promises continue to execute, and then eventually settle, but all their results are ignored. There are ways to avoid this: we can either write additional code to `clearTimeout` (or otherwise cancel) the promises in case of an error, or we can make errors show up as members in the resulting array (see the task below this chapter about it). -````smart header="`Promise.all(iterable)` allows non-promise items in `iterable`" -Normally, `Promise.all(iterable)` accepts an iterable (in most cases an array) of promises. But if any of those objects is not a promise, it's wrapped in `Promise.resolve`. +````smart header="`Promise.all(...)` allows non-promise items in `iterable`" +Normally, `Promise.all(...)` accepts an iterable (in most cases an array) of promises. But if any of those objects is not a promise, it's wrapped in `Promise.resolve`. For instance, here the results are `[1, 2, 3]`: @@ -170,7 +175,7 @@ So we are able to pass non-promise values to `Promise.all` where convenient. ## Promise.race -Similar to `Promise.all` takes an iterable of promises, but instead of waiting for all of them to finish -- waits for the first result (or error), and goes on with it. +Similar to `Promise.all`, it takes an iterable of promises, but instead of waiting for all of them to finish, it waits for the first result (or error), and goes on with it. The syntax is: @@ -194,8 +199,8 @@ So, the first result/error becomes the result of the whole `Promise.race`. After There are 4 static methods of `Promise` class: -1. `Promise.resolve(value)` -- makes a resolved promise with the given value, -2. `Promise.reject(error)` -- makes a rejected promise with the given error, +1. `Promise.resolve(value)` -- makes a resolved promise with the given value. +2. `Promise.reject(error)` -- makes a rejected promise with the given error. 3. `Promise.all(promises)` -- waits for all promises to resolve and returns an array of their results. If any of the given promises rejects, then it becomes the error of `Promise.all`, and all other results are ignored. 4. `Promise.race(promises)` -- waits for the first promise to settle, and its result/error becomes the outcome. diff --git a/6-async/04-promise-api/head.html b/1-js/11-async/05-promise-api/head.html similarity index 100% rename from 6-async/04-promise-api/head.html rename to 1-js/11-async/05-promise-api/head.html diff --git a/1-js/11-async/05-promise-api/iliakan.json b/1-js/11-async/05-promise-api/iliakan.json new file mode 100644 index 000000000..32f89971a --- /dev/null +++ b/1-js/11-async/05-promise-api/iliakan.json @@ -0,0 +1,4 @@ +{ + "name": "iliakan", + "isAdmin": true +} diff --git a/1-js/11-async/05-promise-api/one.js b/1-js/11-async/05-promise-api/one.js new file mode 100644 index 000000000..948a60e07 --- /dev/null +++ b/1-js/11-async/05-promise-api/one.js @@ -0,0 +1,3 @@ +function one() { + alert(1); +} diff --git a/1-js/11-async/05-promise-api/two.js b/1-js/11-async/05-promise-api/two.js new file mode 100644 index 000000000..b04795b86 --- /dev/null +++ b/1-js/11-async/05-promise-api/two.js @@ -0,0 +1,3 @@ +function two() { + alert(2); +} diff --git a/1-js/11-async/06-promisify/article.md b/1-js/11-async/06-promisify/article.md new file mode 100644 index 000000000..e72329ce1 --- /dev/null +++ b/1-js/11-async/06-promisify/article.md @@ -0,0 +1,118 @@ +# Promisification + +Promisification -- is a long word for a simple transform. It's conversion of a function that accepts a callback into a function returning a promise. + +In other words, we create a wrapper-function that does the same, internally calling the original one, but returns a promise. + +Such transforms are often needed in real-life, as many functions and libraries are callback-based. But promises are more convenient. So it makes sense to promisify those. + +For instance, we have `loadScript(src, callback)` from the chapter . + +```js run +function loadScript(src, callback) { + let script = document.createElement('script'); + script.src = src; + + script.onload = () => callback(null, script); + script.onerror = () => callback(new Error(`Script load error for ${src}`)); + + document.head.append(script); +} + +// usage: +// loadScript('path/script.js', (err, script) => {...}) +``` + +Let's promisify it. The new `loadScriptPromise(src)` function will do the same, but accept only `src` (no callback) and return a promise. + +```js +let loadScriptPromise = function(src) { + return new Promise((resolve, reject) => { + loadScript(src, (err, script) => { + if (err) reject(err) + else resolve(script); + }); + }) +} + +// usage: +// loadScriptPromise('path/script.js').then(...) +``` + +Now `loadScriptPromise` fits well in our promise-based code. + +As we can see, it delegates all the work to the original `loadScript`, providing its own callback that translates to promise `resolve/reject`. + +As we may need to promisify many functions, it makes sense to use a helper. + +That's actually very simple -- `promisify(f)` below takes a to-promisify function `f` and returns a wrapper function. + +That wrapper does the same as in the code above: returns a promise and passes the call to the original `f`, tracking the result in a custom callback: + +```js +function promisify(f) { + return function (...args) { // return a wrapper-function + return new Promise((resolve, reject) => { + function callback(err, result) { // our custom callback for f + if (err) { + return reject(err); + } else { + resolve(result); + } + } + + args.push(callback); // append our custom callback to the end of arguments + + f.call(this, ...args); // call the original function + }); + }; +}; + +// usage: +let loadScriptPromise = promisify(loadScript); +loadScriptPromise(...).then(...); +``` + +Here we assume that the original function expects a callback with two arguments `(err, result)`. That's what we encounter most often. Then our custom callback is in exactly the right format, and `promisify` works great for such a case. + +But what if the original `f` expects a callback with more arguments `callback(err, res1, res2)`? + +Here's a modification of `promisify` that returns an array of multiple callback results: + +```js +// promisify(f, true) to get array of results +function promisify(f, manyArgs = false) { + return function (...args) { + return new Promise((resolve, reject) => { + function *!*callback(err, ...results*/!*) { // our custom callback for f + if (err) { + return reject(err); + } else { + // resolve with all callback results if manyArgs is specified + *!*resolve(manyArgs ? results : results[0]);*/!* + } + } + + args.push(callback); + + f.call(this, ...args); + }); + }; +}; + +// usage: +f = promisify(f, true); +f(...).then(arrayOfResults => ..., err => ...) +``` + +In some cases, `err` may be absent at all: `callback(result)`, or there's something exotic in the callback format, then we can promisify such functions manually. + +There are also modules with a bit more flexible promisification functions, e.g. [es6-promisify](https://github.com/digitaldesignlabs/es6-promisify). In Node.js, there's a built-in `util.promisify` function for that. + +```smart +Promisification is a great approach, especially when you use `async/await` (see the next chapter), but not a total replacement for callbacks. + +Remember, a promise may have only one result, but a callback may technically be called many times. + +So promisification is only meant for functions that call the callback once. Further calls will be ignored. +``` diff --git a/1-js/11-async/07-microtask-queue/article.md b/1-js/11-async/07-microtask-queue/article.md new file mode 100644 index 000000000..993042e57 --- /dev/null +++ b/1-js/11-async/07-microtask-queue/article.md @@ -0,0 +1,190 @@ + +# Microtasks and event loop + +Promise handlers `.then`/`.catch`/`.finally` are always asynchronous. + +Even when a Promise is immediately resolved, the code on the lines *below* your `.then`/`.catch`/`.finally` will still execute first. + +Here's the code that demonstrates it: + +```js run +let promise = Promise.resolve(); + +promise.then(() => alert("promise done")); + +alert("code finished"); // this alert shows first +``` + +If you run it, you see `code finished` first, and then `promise done`. + +That's strange, because the promise is definitely done from the beginning. + +Why did the `.then` trigger afterwards? What's going on? + +# Microtasks + +Asynchronous tasks need proper management. For that, the standard specifies an internal queue `PromiseJobs`, more often referred to as "microtask queue" (v8 term). + +As said in the [specification](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues): + +- The queue is first-in-first-out: tasks enqueued first are run first. +- Execution of a task is initiated only when nothing else is running. + +Or, to say that simply, when a promise is ready, its `.then/catch/finally` handlers are put into the queue. They are not executed yet. JavaScript engine takes a task from the queue and executes it, when it becomes free from the current code. + +That's why "code finished" in the example above shows first. + +![](promiseQueue.png) + +Promise handlers always go through that internal queue. + +If there's a chain with multiple `.then/catch/finally`, then every one of them is executed asynchronously. That is, it first gets queued, and executed when the current code is complete and previously queued handlers are finished. + +**What if the order matters for us? How can we make `code finished` work after `promise done`?** + +Easy, just put it into the queue with `.then`: + +```js run +Promise.resolve() + .then(() => alert("promise done!")) + .then(() => alert("code finished")); +``` + +Now the order is as intended. + +## Event loop + +In-browser JavaScript, as well as Node.js, is based on an *event loop*. + +"Event loop" is a process when the engine sleeps and waits for events, then reacts on those and sleeps again. + +Examples of events: +- `mousemove`, a user moved their mouse. +- `setTimeout` handler is to be called. +- an external ` diff --git a/1-js/12-generators-iterators/index.md b/1-js/12-generators-iterators/index.md new file mode 100644 index 000000000..ccc909d1a --- /dev/null +++ b/1-js/12-generators-iterators/index.md @@ -0,0 +1,2 @@ + +# Generators, advanced iteration diff --git a/1-js/13-modules/01-modules-intro/article.md b/1-js/13-modules/01-modules-intro/article.md new file mode 100644 index 000000000..ad4f21068 --- /dev/null +++ b/1-js/13-modules/01-modules-intro/article.md @@ -0,0 +1,375 @@ + +# Modules, introduction + +As our application grows bigger, we want to split it into multiple files, so called 'modules'. +A module usually contains a class or a library of useful functions. + +For a long time, JavaScript existed without a language-level module syntax. That wasn't a problem, because initially scripts were small and simple. So there was no need. + +But eventually scripts became more and more complex, so the community invented a variety of ways to organize code into modules. + +For instance: + +- [AMD](https://en.wikipedia.org/wiki/Asynchronous_module_definition) -- one of the most ancient module systems, initially implemented by the library [require.js](http://requirejs.org/). +- [CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1) -- the module system created for Node.js server. +- [UMD](https://github.com/umdjs/umd) -- one more module system, suggested as a universal one, compatible with AMD and CommonJS. + +Now all these slowly become a part of history, but we still can find them in old scripts. The language-level module system appeared in the standard in 2015, gradually evolved since then, and is now supported by all major browsers and in Node.js. + +## What is a module? + +A module is just a file, a single script, as simple as that. + +Directives `export` and `import` allow to interchange functionality between modules: + +- `export` keyword labels variables and functions that should be accessible from outside the file. +- `import` allows to import functionality from other modules. + +For instance, if we have a file `sayHi.js` exporting a function: + +```js +// 📁 sayHi.js +export function sayHi(user) { + alert(`Hello, ${user}!`); +} +``` + +...Then another file may import and use it: + +```js +// 📁 main.js +import {sayHi} from './sayHi.js'; + +alert(sayHi); // function... +sayHi('John'); // Hello, John! +``` + +In this tutorial we concentrate on the language itself, but we use browser as the demo environment, so let's see how modules work in the browser. + +To use modules, we must set the attribute ` +``` + +Here we can see it in the browser, but the same is true for any module. + +### Module-level scope + +Each module has its own top-level scope. In other words, top-level variables and functions from a module are not seen in other scripts. + +In the example below, two scripts are imported, and `hello.js` tries to use `user` variable declared in `user.js`, and fails: + +[codetabs src="scopes" height="140" current="index.html"] + +Modules are expected to `export` what they want to be accessible from outside and `import` what they need. + +So we should import `user.js` directly into `hello.js` instead of `index.html`. + +That's the correct variant: + +[codetabs src="scopes-working" height="140" current="hello.js"] + +In the browser, independant top-level scope also exists for each ` + + +``` + +If we really need to make a "global" in-browser variable, we can explicitly assign it to `window` and access as `window.user`. But that's an exception requiring a good reason. + +### A module code is evaluated only the first time when imported + +If the same module is imported into multiple other places, its code is executed only the first time, then exports are given to all importers. + +That has important consequences. Let's see that on examples. + +First, if executing a module code brings side-effects, like showing a message, then importing it multiple times will trigger it only once -- the first time: + +```js +// 📁 alert.js +alert("Module is evaluated!"); +``` + +```js +// Import the same module from different files + +// 📁 1.js +import `./alert.js`; // Module is evaluated! + +// 📁 2.js +import `./alert.js`; // (nothing) +``` + +In practice, top-level module code is mostly used for initialization. We create data structures, pre-fill them, and if we want something to be reusable -- export it. + +Now, a more advanced example. + +Let's say, a module exports an object: + +```js +// 📁 admin.js +export let admin = { + name: "John" +}; +``` + +If this module is imported from multiple files, the module is only evaluated the first time, `admin` object is created, and then passed to all further importers. + +All importers get exactly the one and only `admin` object: + +```js +// 📁 1.js +import {admin} from './admin.js'; +admin.name = "Pete"; + +// 📁 2.js +import {admin} from './admin.js'; +alert(admin.name); // Pete + +*!* +// Both 1.js and 2.js imported the same object +// Changes made in 1.js are visible in 2.js +*/!* +``` + +So, let's reiterate -- the module is executed only once. Exports are generated, and then they are shared between importers, so if something changes the `admin` object, other modules will see that . + +Such behavior is great for modules that require configuration. We can set required properties on the first import, and then in further imports it's ready. + +For instance, `admin.js` module may provide certain functionality, but expect the credentials to come into the `admin` object from outside: + +```js +// 📁 admin.js +export let admin = { }; + +export function sayHi() { + alert(`Ready to serve, ${admin.name}!`); +} +``` + +Now, in `init.js`, the first script of our app, we set `admin.name`. Then everyone will see it, including calls made from inside `admin.js` itself: + +```js +// 📁 init.js +import {admin} from './admin.js'; +admin.name = "Pete"; +``` + +```js +// 📁 other.js +import {admin, sayHi} from './admin.js'; + +alert(admin.name); // *!*Pete*/!* + +sayHi(); // Ready to serve, *!*Pete*/!*! +``` + +### import.meta + +The object `import.meta` contains the information about the current module. + +Its content depends on the environment. In the browser, it contains the url of the script, or a current webpage url if inside HTML: + +```html run height=0 + +``` + +### Top-level "this" is undefined + +That's kind of a minor feature, but for completeness we should mention it. + +In a module, top-level `this` is undefined, as opposed to a global object in non-module scripts: + +```html run height=0 + + + +``` + +## Browser-specific features + +There are also several browser-specific differences of scripts with `type="module"` compared to regular ones. + +You may want skip those for now if you're reading for the first time, or if you don't use JavaScript in a browser. + +### Module scripts are deferred + +Module scripts are *always* deferred, same effect as `defer` attribute (described in the chapter [](info:script-async-defer)), for both external and inline scripts. + +In other words: +- external module scripts ` + + + + +``` + +Please note: the second script actually works before the first! So we'll see `undefined` first, and then `object`. + +That's because modules are deferred, so way wait for the document to be processed. The regular scripts runs immediately, so we saw its output first. + +When using modules, we should be aware that HTML-document can show up before the JavaScript application is ready. Some functionality may not work yet. We should put transparent overlays or "loading indicators", or otherwise ensure that the visitor won't be confused because of it. + +### Async works on inline scripts + +Async attribute ` +``` + +### External scripts + +There are two notable differences of external module scripts: + +1. External scripts with same `src` run only once: + ```html + + + + ``` + +2. External scripts that are fetched from another domain require [CORS](mdn:Web/HTTP/CORS) headers. In other words, if a module script is fetched from another domain, the remote server must supply a header `Access-Control-Allow-Origin: *` (may use fetching domain instead of `*`) to indicate that the fetch is allowed. + ```html + + + + ``` + + That ensures better security by default. + +### No bare modules allowed + +In the browser, in scripts (not in HTML), `import` must get either a relative or absolute URL. So-called "bare" modules, without a path, are not allowed. + +For instance, this `import` is invalid: +```js +import {sayHi} from 'sayHi'; // Error, "bare" module +// must be './sayHi.js' or wherever the module is +``` + +Certain environments, like Node.js or bundle tools allow bare modules, as they have own ways for finding modules and hooks to fine-tune them. But browsers do not support bare modules yet. + +### Compatibility, "nomodule" + +Old browsers do not understand `type="module"`. Scripts of the unknown type are just ignored. For them, it's possible to provide a fallback using `nomodule` attribute: + +```html run + + + +``` + +If we use bundle tools, then as modules are bundled together, their `import/export` statements are replaced by special bundler calls, so the resulting build does not require `type="module"`, and we can put it into a regular script: + +```html + + +``` + +## Build tools + +In real-life, browser modules are rarely used in their "raw" form. Usually, we bundle them together with a special tool such as [Webpack](https://webpack.js.org/) and deploy to the production server. + +One of the benefits of using bundlers -- they give more control over how modules are resolved, allowing bare modules and much more, like CSS/HTML modules. + +Build tools do the following: + +1. Take a "main" module, the one intended to be put in ` diff --git a/1-js/13-modules/01-modules-intro/say.view/say.js b/1-js/13-modules/01-modules-intro/say.view/say.js new file mode 100644 index 000000000..198a3be6d --- /dev/null +++ b/1-js/13-modules/01-modules-intro/say.view/say.js @@ -0,0 +1,3 @@ +export function sayHi(user) { + return `Hello, ${user}!`; +} diff --git a/1-js/13-modules/01-modules-intro/scopes-working.view/hello.js b/1-js/13-modules/01-modules-intro/scopes-working.view/hello.js new file mode 100644 index 000000000..6c087ea81 --- /dev/null +++ b/1-js/13-modules/01-modules-intro/scopes-working.view/hello.js @@ -0,0 +1,3 @@ +import {user} from './user.js'; + +document.body.innerHTML = user; // John diff --git a/1-js/13-modules/01-modules-intro/scopes-working.view/index.html b/1-js/13-modules/01-modules-intro/scopes-working.view/index.html new file mode 100644 index 000000000..b78f75912 --- /dev/null +++ b/1-js/13-modules/01-modules-intro/scopes-working.view/index.html @@ -0,0 +1,2 @@ + + diff --git a/1-js/13-modules/01-modules-intro/scopes-working.view/user.js b/1-js/13-modules/01-modules-intro/scopes-working.view/user.js new file mode 100644 index 000000000..d289329c6 --- /dev/null +++ b/1-js/13-modules/01-modules-intro/scopes-working.view/user.js @@ -0,0 +1 @@ +export let user = "John"; diff --git a/1-js/13-modules/01-modules-intro/scopes.view/hello.js b/1-js/13-modules/01-modules-intro/scopes.view/hello.js new file mode 100644 index 000000000..714aafa1f --- /dev/null +++ b/1-js/13-modules/01-modules-intro/scopes.view/hello.js @@ -0,0 +1 @@ +alert(user); // no such variable (each module has independent variables) diff --git a/1-js/13-modules/01-modules-intro/scopes.view/index.html b/1-js/13-modules/01-modules-intro/scopes.view/index.html new file mode 100644 index 000000000..a87e96fdf --- /dev/null +++ b/1-js/13-modules/01-modules-intro/scopes.view/index.html @@ -0,0 +1,3 @@ + + + diff --git a/1-js/13-modules/01-modules-intro/scopes.view/user.js b/1-js/13-modules/01-modules-intro/scopes.view/user.js new file mode 100644 index 000000000..12ec850d9 --- /dev/null +++ b/1-js/13-modules/01-modules-intro/scopes.view/user.js @@ -0,0 +1 @@ +let user = "John"; diff --git a/1-js/13-modules/02-import-export/article.md b/1-js/13-modules/02-import-export/article.md new file mode 100644 index 000000000..bf31065bc --- /dev/null +++ b/1-js/13-modules/02-import-export/article.md @@ -0,0 +1,438 @@ + +# Export and Import + +Export and import directives are very versatile. + +In the previous chapter we saw a simple use, now let's explore more examples. + +## Export before declarations + +We can label any declaration as exported by placing `export` before it, be it a variable, function or a class. + +For instance, here all exports are valid: + +```js +// export an array +*!*export*/!* let months = ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + +// export a constant +*!*export*/!* const MODULES_BECAME_STANDARD_YEAR = 2015; + +// export a class +*!*export*/!* class User { + constructor(name) { + this.name = name; + } +} +``` + +````smart header="No semicolons after export class/function" +Please note that `export` before a class or a function does not make it a [function expression](info:function-expressions-arrows). It's still a function declaration, albeit exported. + +Most JavaScript style guides recommend semicolons after statements, but not after function and class declarations. + +That's why there should be no semicolons at the end of `export class` and `export function`. + +```js +export function sayHi(user) { + alert(`Hello, ${user}!`); +} *!* // no ; at the end */!* +``` + +```` + +## Export apart from declarations + +Also, we can put `export` separately. + +Here we first declare, and then export: + +```js +// 📁 say.js +function sayHi(user) { + alert(`Hello, ${user}!`); +} + +function sayBye(user) { + alert(`Bye, ${user}!`); +} + +*!* +export {sayHi, sayBye}; // a list of exported variables +*/!* +``` + +...Or, technically we could put `export` above functions as well. + +## Import * + +Usually, we put a list of what to import into `import {...}`, like this: + +```js +// 📁 main.js +*!* +import {sayHi, sayBye} from './say.js'; +*/!* + +sayHi('John'); // Hello, John! +sayBye('John'); // Bye, John! +``` + +But if the list is long, we can import everything as an object using `import * as `, for instance: + +```js +// 📁 main.js +*!* +import * as say from './say.js'; +*/!* + +say.sayHi('John'); +say.sayBye('John'); +``` + +At first sight, "import everything" seems such a cool thing, short to write, why should we ever explicitly list what we need to import? + +Well, there are few reasons. + +1. Modern build tools ([webpack](http://webpack.github.io) and others) bundle modules together and optimize them to speedup loading and remove unused stuff. + + Let's say, we added a 3rd-party library `lib.js` to our project with many functions: + ```js + // 📁 lib.js + export function sayHi() { ... } + export function sayBye() { ... } + export function becomeSilent() { ... } + ``` + + Now if we in fact need only one of them in our project: + ```js + // 📁 main.js + import {sayHi} from './lib.js'; + ``` + ...Then the optimizer will automatically detect it and totally remove the other functions from the bundled code, thus making the build smaller. That is called "tree-shaking". + +2. Explicitly listing what to import gives shorter names: `sayHi()` instead of `lib.sayHi()`. +3. Explicit imports give better overview of the code structure: what is used and where. It makes code support and refactoring easier. + +## Import "as" + +We can also use `as` to import under different names. + +For instance, let's import `sayHi` into the local variable `hi` for brevity, and same for `sayBye`: + +```js +// 📁 main.js +*!* +import {sayHi as hi, sayBye as bye} from './say.js'; +*/!* + +hi('John'); // Hello, John! +bye('John'); // Bye, John! +``` + +## Export "as" + +The similar syntax exists for `export`. + +Let's export functions as `hi` and `bye`: + +```js +// 📁 say.js +... +export {sayHi as hi, sayBye as bye}; +``` + +Now `hi` and `bye` are official names for outsiders: + +```js +// 📁 main.js +import * as say from './say.js'; + +say.hi('John'); // Hello, John! +say.bye('John'); // Bye, John! +``` + +## export default + +So far, we've seen how to import/export multiple things, optionally "as" other names. + +In practice, modules contain either: +- A library, pack of functions, like `lib.js`. +- Or an entity, like `class User` is described in `user.js`, the whole module has only this class. + +Mostly, the second approach is preferred, so that every "thing" resides in its own module. + +Naturally, that requires a lot of files, as everything wants its own module, but that's not a problem at all. Actually, code navigation becomes easier, if files are well-named and structured into folders. + +Modules provide special `export default` syntax to make "one thing per module" way look better. + +It requires following `export` and `import` statements: + +1. Put `export default` before the "main export" of the module. +2. Call `import` without curly braces. + +For instance, here `user.js` exports `class User`: + +```js +// 📁 user.js +export *!*default*/!* class User { // just add "default" + constructor(name) { + this.name = name; + } +} +``` + +...And `main.js` imports it: + +```js +// 📁 main.js +import *!*User*/!* from './user.js'; // not {User}, just User + +new User('John'); +``` + +Imports without curly braces look nicer. A common mistake when starting to use modules is to forget curly braces at all. So, remember, `import` needs curly braces for named imports and doesn't need them for the default one. + +| Named export | Default export | +|--------------|----------------| +| `export class User {...}` | `export default class User {...}` | +| `import {User} from ...` | `import User from ...`| + +Naturally, there may be only one "default" export per file. + +We may have both default and named exports in a single module, but in practice people usually don't mix them. A module has either named exports or the default one. + +**Another thing to note is that named exports must (naturally) have a name, while `export default` may be anonymous.** + +For instance, these are all perfectly valid default exports: + +```js +export default class { // no class name + constructor() { ... } +} + +export default function(user) { // no function name + alert(`Hello, ${user}!`); +} + +// export a single value, without making a variable +export default ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; +``` + +That's fine, because `export default` is only one per file, so `import` always knows what to import. + Contrary to that, omitting a name for named imports would be an error: + +```js +export class { // Error! (non-default export needs a name) + constructor() {} +} +``` + +### "Default" alias + +The "default" word is a kind of "alias" for the default export, for scenarios when we need to reference it somehow. + +For example, if we already have a function declared, that's how to `export default` it: + +```js +function sayHi(user) { + alert(`Hello, ${user}!`); +} + +export {sayHi as default}; // same as if we added "export default" before the function +``` + +Or, let's say a module `user.js` exports one main "default" thing and a few named ones (rarely the case, but happens): + +```js +// 📁 user.js +export default class User { + constructor(name) { + this.name = name; + } +} + +export function sayHi(user) { + alert(`Hello, ${user}!`); +} +``` + +Here's how to import the default export along with a named one: + +```js +// 📁 main.js +import {*!*default as User*/!*, sayHi} from './user.js'; + +new User('John'); +``` + +Or, if we consider importing `*` as an object, then the `default` property is exactly the default export: + +```js +// 📁 main.js +import * as user from './user.js'; + +let User = user.default; +new User('John'); +``` + + +### Should I use default exports? + +One should be careful about using default exports, because they are somewhat more different to maintain. + +Named exports are explicit. They exactly name what they import, so we have that information from them, that's a good thing. + +Also, named exports enforce us to use exactly the right name to import: + +```js +import {User} from './user.js'; +``` + +For default exports, we need to create a name on our own: + +```js +import MyUser from './user.js'; // could be import Anything..., and it'll work +``` + +So, there's a little bit more freedom that can be abused, so that team members may use different names for the same thing. + +Usually, to avoid that and keep the code consistent, there's a rule that imported variables should correspond to file names, e.g: + +```js +import User from './user.js'; +import LoginForm from './loginForm.js'; +import func from '/path/to/func.js'; +... +``` + +Another solution would be to use named exports everywhere. Even if only a single thing is exported, it's still exported under a name, without `default`. + +That also makes re-export (see below) a little bit easier. + +## Re-export + +"Re-export" syntax `export ... from ...` allows to import things and immediately export them (possibly under another name), like this: + +```js +export {sayHi} from './say.js'; +export {default as User} from './user.js'; +``` + +What's the point, why that's needed? Let's see a practical use case. + +Imagine, we're writing a "package": a folder with a lot of modules, mostly needed internally, with some of the functionality exported outside (tools like NPM allow to publish and distribute packages, but here it doesn't matter). + +A directory structure could be like this: +``` +auth/ + index.js + user.js + helpers.js + tests/ + login.js + providers/ + github.js + facebook.js + ... +``` + +We'd like to expose the package functionality via a single entry point, the "main file" `auth/index.js`, to be used like this: + +```js +import {login, logout} from 'auth/index.js' +``` + +The idea is that outsiders, developers who use our package, should not meddle with its internal structure. They should not search for files inside our package folder. We export only what's necessary in `auth/index.js` and keep the rest hidden from prying eyes. + +Now, as the actual exported functionality is scattered among the package, we can gather and "re-export" it in `auth/index.js`: + +```js +// 📁 auth/index.js +import {login, logout} from './helpers.js'; +export {login, logout}; + +import User from './user.js'; +export {User}; + +import Github from './providers/github.js'; +export {Github}; +... +``` + +"Re-exporting" is just a shorter notation for that: + +```js +// 📁 auth/index.js +export {login, logout} from './helpers.js'; +// or, to re-export all helpers, we could use: +// export * from './helpers.js'; + +export {default as User} from './user.js'; + +export {default as Github} from './providers/github.js'; +... +``` + +````warn header="Re-exporting default is tricky" +Please note: `export User from './user.js'` won't work. It's actually a syntax error. To re-export the default export, we must mention it explicitly `{default as ...}`, like in the example above. + +Also, there's another oddity: `export * from './user.js'` re-exports only named exports, exluding the default one. Once again, we need to mention it explicitly. + +For instance, to re-export everything, two statements will be necessary: +```js +export * from './module.js'; // to re-export named exports +export {default} from './module.js'; // to re-export default +``` + +The default should be mentioned explicitly only when re-exporting: `import * as obj` works fine. It imports the default export as `obj.default`. So there's a slight asymmetry between import and export constructs here. +```` + +## Summary + +There are following types of `export`: + +- Before declaration: + - `export [default] class/function/variable ...` +- Standalone: + - `export {x [as y], ...}`. +- Re-export: + - `export {x [as y], ...} from "mod"` + - `export * from "mod"` (doesn't re-export default). + - `export {default [as y]} from "mod"` (re-export default). + +Import: + +- Named exports from module: + - `import {x [as y], ...} from "mod"` +- Default export: + - `import x from "mod"` + - `import {default as x} from "mod"` +- Everything: + - `import * as obj from "mod"` +- Only fetch/evalute the module, don't import: + - `import "mod"` + +We can put import/export statements below or after other code, that doesn't matter. + +So this is technically fine: +```js +sayHi(); + +import {sayHi} from './say.js'; // import at the end of the file +``` + +In practice imports are usually at the start of the file, but that's only for better convenience. + +**Please note that import/export statements don't work if inside `{...}`.** + +A conditional import, like this, won't work: +```js +if (something) { + import {sayHi} from "./say.js"; // Error: import must be at top level +} +``` + +...But what if we really need to import something conditionally? Or at the right time? Like, load a module upon request, when it's really needed? + +We'll see dynamic imports in the next chapter. diff --git a/1-js/13-modules/03-modules-dynamic-imports/article.md b/1-js/13-modules/03-modules-dynamic-imports/article.md new file mode 100644 index 000000000..d8de2bcfa --- /dev/null +++ b/1-js/13-modules/03-modules-dynamic-imports/article.md @@ -0,0 +1,54 @@ + +# Dynamic imports + +Export and import statements that we covered in previous chaters are called "static". + +That's because they are indeed static. The syntax is very strict. + +First, we can't dynamicaly generate any parameters of `import`. + +The module path must be a primitive string, can't be a function call. This won't work: + +```js +import ... from *!*getModuleName()*/!*; // Error, only from "string" is allowed +``` + +Second, we can't import conditionally or at run-time: + +```js +if(...) { + import ...; // Error, not allowed! +} + +{ + import ...; // Error, we can't put import in any block +} +``` + +That's because, import/export aim to provide a backbone for the code structure. That's a good thing, as code structure can be analyzed, modules can be gathered and bundled together, unused exports can be removed (tree-shaken). That's possible only because everything is fixed. + +But how do we import a module dynamically, on-demand? + +## The import() function + +The `import(module)` function can be called from anywhere. It returns a promise that resolves into a module object. + +The usage pattern looks like this: + +```js run +let modulePath = prompt("Module path?"); + +import(modulePath) + .then(obj => ) + .catch(err => ) +``` + +Or, we could use `let module = await import(modulePath)` if inside an async function. + +Like this: + +[codetabs src="say" current="index.html"] + +So, dynamic imports are very simple to use. + +Also, dynamic imports work in regular scripts, they don't require `script type="module"`. diff --git a/1-js/13-modules/03-modules-dynamic-imports/say.view/index.html b/1-js/13-modules/03-modules-dynamic-imports/say.view/index.html new file mode 100644 index 000000000..80909cf94 --- /dev/null +++ b/1-js/13-modules/03-modules-dynamic-imports/say.view/index.html @@ -0,0 +1,10 @@ + + + diff --git a/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js b/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js new file mode 100644 index 000000000..cff234b7c --- /dev/null +++ b/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js @@ -0,0 +1,11 @@ +export function hi() { + alert(`Hello`); +} + +export function bye() { + alert(`Bye`); +} + +export default function() { + alert("Module loaded (export default)!"); +} diff --git a/1-js/13-modules/index.md b/1-js/13-modules/index.md new file mode 100644 index 000000000..78fb060e8 --- /dev/null +++ b/1-js/13-modules/index.md @@ -0,0 +1,2 @@ + +# Modules diff --git a/1-js/plan3.txt b/1-js/plan3.txt deleted file mode 100644 index 4d553174f..000000000 --- a/1-js/plan3.txt +++ /dev/null @@ -1,5 +0,0 @@ -todo: - -intl? -proxy? -eval? diff --git a/10-misc/12-mutation-observer/article.md b/10-misc/12-mutation-observer/article.md new file mode 100644 index 000000000..1045e87a8 --- /dev/null +++ b/10-misc/12-mutation-observer/article.md @@ -0,0 +1,249 @@ + +# Mutation observer + +`MutationObserver` is a built-in object that observes a DOM element and fires a callback in case of changes. + +We'll first see syntax, and then explore a real-world use case. + +## Syntax + +`MutationObserver` is easy to use. + +First, we create an observer with a callback-function: + +```js +let observer = new MutationObserver(callback); +``` + +And then attach it to a DOM node: + +```js +observer.observe(node, config); +``` + +`config` is an object with boolean options "what kind of changes to react on": +- `childList` -- changes in the direct children of `node`, +- `subtree` -- in all descendants of `node`, +- `attributes` -- attributes of `node`, +- `attributeOldValue` -- record the old value of attribute (infers `attributes`), +- `characterData` -- whether to observe `node.data` (text content), +- `characterDataOldValue` -- record the old value of `node.data` (infers `characterData`), +- `attributeFilter` -- an array of attribute names, to observe only selected ones. + +Then after any changes, the `callback` is executed, with a list of [MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) objects as the first argument, and the observer itself as the second argument. + +[MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) objects have properties: + +- `type` -- mutation type, one of + - `"attributes"` (attribute modified) + - `"characterData"` (data modified) + - `"childList"` (elements added/removed), +- `target` -- where the change occured: an element for "attributes", or text node for "characterData", or an element for a "childList" mutation, +- `addedNodes/removedNodes` -- nodes that were added/removed, +- `previousSibling/nextSibling` -- the previous and next sibling to added/removed nodes, +- `attributeName/attributeNamespace` -- the name/namespace (for XML) of the changed attribute, +- `oldValue` -- the previous value, only for attribute or text changes. + + +For example, here's a `
` with `contentEditable` attribute. That attribute allows us to focus on it and edit. + +```html run +
Edit me, please
+ + +``` + +If we change the text inside `me`, we'll get a single mutation: + +```js +mutationRecords = [{ + type: "characterData", + oldValue: "me", + target: , + // other properties empty +}]; +``` + +If we select and remove the `me` altogether, we'll get multiple mutations: + +```js +mutationRecords = [{ + type: "childList", + target: , + removedNodes: [], + nextSibling: , + previousSibling: + // other properties empty +}, { + type: "characterData" + target: + // ...details depend on how the browser handles the change + // it may coalesce two adjacent text nodes "Edit " and ", please" into one node + // or it can just delete the extra space after "Edit". + // may be one mutation or a few +}]; +``` + +## Observer use case + +When `MutationObserver` is needed? Is there a scenario when such thing can be useful? + +Sure, we can track something like `contentEditable` and create "undo/redo" stack, but here's an example where `MutationObserver` is good from architectural standpoint. + +Let's say we're making a website about programming, like this one. Naturally, articles and other materials may contain source code snippets. + +An HTML code snippet looks like this: +```html +... +

+  // here's the code
+  let hello = "world";
+
+... +``` + +There's also a JavaScript highlighting library, e.g. [Prism.js](https://prismjs.com/). A call to `Prism.highlightElem(pre)` examines the contents of such `pre` elements and adds colored syntax highlighting, similar to what you in examples here, this page. + +Generally, when a page loads, e.g. at the bottom of the page, we can search for elements `pre[class*="language"]` and call `Prism.highlightElem` on them: + +```js +// highlight all code snippets on the page +document.querySelectorAll('pre[class*="language"]').forEach(Prism.highlightElem); +``` + +Now the `
` snippet looks like this (without line numbers by default):
+
+```js
+// here's the code
+let hello = "world";
+```
+
+Everything's simple so far, right? There are `
` code snippets in HTML, we highlight them.
+
+Now let's go on. Let's say we're going to dynamically fetch materials from a server. We'll study methods for that [later in the tutorial](info:fetch-basics). For now it only matters that we fetch an HTML article from a webserver and display it on demand:
+
+```js
+let article = /* fetch new content from server */
+articleElem.innerHTML = article;
+```
+
+The new `article` HTML may contain code snippets. We need to call `Prism.highlightElem` on them, otherwise they won't get highlighted.
+
+**Who's responsibility is to call `Prism.highlightElem` for a dynamically loaded article?**
+
+We could append that call to the code that loads an article, like this:
+
+```js
+let article = /* fetch new content from server */
+articleElem.innerHTML = article;
+
+*!*
+let snippets = articleElem.querySelectorAll('pre[class*="language-"]');
+snippets.forEach(Prism.highlightElem);
+*/!*
+```
+
+...But imagine, we have many places where we load contents with code: articles, quizzes, forum posts. Do we need to put the highlighting call everywhere? Then we need to be careful, not to forget about it.
+
+And what if we load the content into a third-party engine? E.g. we have a forum written by someone else, that loads contents dynamically, and we'd like to add syntax highlighting to it. No one likes to patch third-party scripts.
+
+Luckily, there's another option.
+
+We can use `MutationObserver` to automatically detect code snippets inserted in the page and highlight them.
+
+So we'll handle the highlighting functionality in one place, relieving us from the need to integrate it.
+
+## Dynamic highlight demo
+
+Here's the working example.
+
+If you run this code, it starts observing the element below and highlighting any code snippets that appear there:
+
+```js run
+let observer = new MutationObserver(mutations => {
+
+  for(let mutation of mutations) {
+    // examine new nodes
+
+    for(let node of mutation.addedNodes) {
+      // skip newly added text nodes
+      if (!(node instanceof HTMLElement)) continue;
+
+      // check the inserted element for being a code snippet
+      if (node.matches('pre[class*="language-"]')) {
+        Prism.highlightElement(node);
+      }
+
+      // search its subtree for code snippets
+      for(let elem of node.querySelectorAll('pre[class*="language-"]')) {
+        Prism.highlightElement(elem);
+      }
+    }
+  }
+
+});
+
+let demoElem = document.getElementById('highlight-demo');
+
+observer.observe(demoElem, {childList: true, subtree: true});
+```
+
+

Demo element with id="highlight-demo", obverved by the example above.

+ +The code below populates `innerHTML`. If you've run the code above, snippets will get highlighted: + +```js run +let demoElem = document.getElementById('highlight-demo'); + +// dynamically insert content with code snippets +demoElem.innerHTML = `A code snippet is below: +
 let hello = "world!"; 
+
Another one:
+
+
.class { margin: 5px; } 
+
+`; +``` + +Now we have `MutationObserver` that can track all highlighting in observed elements or the whole `document`. We can add/remove code snippets in HTML without thinking about it. + + +## Garbage collection + +Observers use weak references to nodes internally. That is: if a node is removed from DOM, and becomes unreachable, then it becomes garbage collected, an observer doesn't prevent that. + +Still, we can release observers any time: + +- `observer.disconnect()` -- stops the observation. + +Additionally: + +- `mutationRecords = observer.takeRecords()` -- gets a list of unprocessed mutation records, those that happened, but the callback did not handle them. + +```js +// we're going to disconnect the observer +// it might have not yet handled some mutations +let mutationRecords = observer.takeRecords(); +// process mutationRecords + +// now all handled, disconnect +observer.disconnect(); +``` + +## Summary + +`MutationObserver` can react on changes in DOM: attributes, added/removed elements, text content. + +We can use it to track changes introduced by other parts of our own or 3rd-party code. + +For example, to post-process dynamically inserted content, as demo `innerHTML`, like highlighting in the example above. diff --git a/10-misc/index.md b/10-misc/index.md new file mode 100644 index 000000000..65ab3188a --- /dev/null +++ b/10-misc/index.md @@ -0,0 +1,4 @@ + +# Miscellaneous + +Not yet categorized articles. diff --git a/2-ui/1-document/01-browser-environment/article.md b/2-ui/1-document/01-browser-environment/article.md index 09f553436..fbc85304b 100644 --- a/2-ui/1-document/01-browser-environment/article.md +++ b/2-ui/1-document/01-browser-environment/article.md @@ -4,7 +4,11 @@ Il linguaggio JavaScript è stato inizialmente creato per i browser. Da allora, La piattaforma di utilizzo può essere un browser, un web-server, una lavatrice o un qualunque altro tipo di *host*. Ognuno di essi fornisce delle funzionalità specifiche alla piattaforma stessa. Secondo la specifica JavaScript questa è la definizione di *ambiente host* +<<<<<<< HEAD Un ambiente host, oltre alle funzionalità core del linguaggio, fornisce oggetti e funzioni specifiche della piattaforma. I browser web, ad esempio, permettono di interagire con le pagine web, mentre Node.JS fornisce funzionalità dedicate al server e così via. +======= +A host environment provides platform-specific objects and functions additional to the language core. Web browsers give a means to control web pages. Node.js provides server-side features, and so on. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb Di seguito una panoramica di cosa succede quando JavaScript viene eseguito nel browser: diff --git a/2-ui/1-document/01-browser-environment/windowObjects.png b/2-ui/1-document/01-browser-environment/windowObjects.png index 81803bd09..5408ecb15 100644 Binary files a/2-ui/1-document/01-browser-environment/windowObjects.png and b/2-ui/1-document/01-browser-environment/windowObjects.png differ diff --git a/2-ui/1-document/01-browser-environment/windowObjects@2x.png b/2-ui/1-document/01-browser-environment/windowObjects@2x.png index e6dae7c3d..d84b380d5 100644 Binary files a/2-ui/1-document/01-browser-environment/windowObjects@2x.png and b/2-ui/1-document/01-browser-environment/windowObjects@2x.png differ diff --git a/2-ui/1-document/02-dom-nodes/article.md b/2-ui/1-document/02-dom-nodes/article.md index f3a9faaed..e1616c264 100644 --- a/2-ui/1-document/02-dom-nodes/article.md +++ b/2-ui/1-document/02-dom-nodes/article.md @@ -181,7 +181,11 @@ drawHtmlTree(node6, 'div.domtree', 690, 500); Vediamo ora un nuovo tipo di nodo -- *nodo commento*, etichettato come `#comment`. +<<<<<<< HEAD Potremmo chiederci "perché il commento viene aggiunto al DOM? Non incide sul risultato grafico finale". Esiste in realtà una regola che se qualcosa è nell'HTML, allora deve essere presente anche nell'alberatura del DOM. +======= +We may think -- why is a comment added to the DOM? It doesn't affect the visual representation in any way. But there's a rule -- if something's in HTML, then it also must be in the DOM tree. +>>>>>>> 273e47b70a14ae7a8b882b8d2543e581b000eefb **Tutto ciò che è presente nell'HTML, anche i commenti, finisce per essere parte del DOM.** diff --git a/2-ui/1-document/03-dom-navigation/article.md b/2-ui/1-document/03-dom-navigation/article.md index 976e595a2..1e1e4058c 100644 --- a/2-ui/1-document/03-dom-navigation/article.md +++ b/2-ui/1-document/03-dom-navigation/article.md @@ -7,11 +7,11 @@ libs: # Walking the DOM -The DOM allows to do anything with elements and their contents, but first we need to reach the corresponding DOM object, get it into a variable, and then we are able to modify it. +The DOM allows us to do anything with elements and their contents, but first we need to reach the corresponding DOM object, get it into a variable, and then we are able to modify it. All operations on the DOM start with the `document` object. From it we can access any node. -Here's a picture of links that allow to travel between DOM nodes: +Here's a picture of links that allow for travel between DOM nodes: ![](dom-links.png) @@ -155,9 +155,9 @@ The first thing is nice. The second is tolerable, because we can use `Array.from ```warn header="DOM collections are read-only" DOM collections, and even more -- *all* navigation properties listed in this chapter are read-only. -We can't replace a child by something else assigning `childNodes[i] = ...`. +We can't replace a child by something else by assigning `childNodes[i] = ...`. -Changing DOM needs other methods, we'll see them in the next chapter. +Changing DOM needs other methods. We will see them in the next chapter. ``` ```warn header="DOM collections are live" @@ -237,7 +237,12 @@ alert( document.documentElement.parentElement ); // null In other words, the `documentElement` (``) is the root node. Formally, it has `document` as its parent. But `document` is not an element node, so `parentNode` returns it and `parentElement` does not. -Sometimes that matters when we're walking over the chain of parents and call a method on each of them, but `document` doesn't have it, so we exclude it. +This loop travels up from an arbitrary element `elem` to ``, but not to the `document`: +```js +while(elem = elem.parentElement) { + alert( elem ); // parent chain till +} +``` ```` Let's modify one of the examples above: replace `childNodes` with `children`. Now it shows only elements: @@ -309,7 +314,7 @@ An example of usage: The specification: [tabular data](https://html.spec.whatwg.org/multipage/tables.html). -There are also additional navigation properties for HTML forms. We'll look at them later when start working with forms. +There are also additional navigation properties for HTML forms. We'll look at them later when we start working with forms. # Summary diff --git a/2-ui/1-document/03-dom-navigation/dom-links-elements.png b/2-ui/1-document/03-dom-navigation/dom-links-elements.png index f7eef5d1f..c0e2f6941 100644 Binary files a/2-ui/1-document/03-dom-navigation/dom-links-elements.png and b/2-ui/1-document/03-dom-navigation/dom-links-elements.png differ diff --git a/2-ui/1-document/03-dom-navigation/dom-links-elements@2x.png b/2-ui/1-document/03-dom-navigation/dom-links-elements@2x.png index cf4e220cf..663471177 100644 Binary files a/2-ui/1-document/03-dom-navigation/dom-links-elements@2x.png and b/2-ui/1-document/03-dom-navigation/dom-links-elements@2x.png differ diff --git a/2-ui/1-document/03-dom-navigation/dom-links.png b/2-ui/1-document/03-dom-navigation/dom-links.png index 25b25b377..d6b5ddf96 100644 Binary files a/2-ui/1-document/03-dom-navigation/dom-links.png and b/2-ui/1-document/03-dom-navigation/dom-links.png differ diff --git a/2-ui/1-document/03-dom-navigation/dom-links@2x.png b/2-ui/1-document/03-dom-navigation/dom-links@2x.png index 84cd33d4e..827552f65 100644 Binary files a/2-ui/1-document/03-dom-navigation/dom-links@2x.png and b/2-ui/1-document/03-dom-navigation/dom-links@2x.png differ diff --git a/2-ui/1-document/04-searching-elements-dom/article.md b/2-ui/1-document/04-searching-elements-dom/article.md index 3a7a9aeee..18ce24eba 100644 --- a/2-ui/1-document/04-searching-elements-dom/article.md +++ b/2-ui/1-document/04-searching-elements-dom/article.md @@ -1,13 +1,14 @@ -# Searching: getElement* and querySelector* +# Searching: getElement*, querySelector* DOM navigation properties are great when elements are close to each other. What if they are not? How to get an arbitrary element of the page? There are additional searching methods for that. + ## document.getElementById or just id If an element has the `id` attribute, then there's a global variable by the name from that `id`. -We can use it to access the element, like this: +We can use it to immediately access the element no matter where it is: ```html run
@@ -24,7 +25,9 @@ We can use it to access the element, like this: ``` -That's unless we declare the same-named variable by our own: +The behavior is described [in the specification](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem), but it is supported mainly for compatibility. The browser tries to help us by mixing namespaces of JS and DOM. Good for very simple scripts, but there may be name conflicts. Also, when we look in JS and don't have HTML in view, it's not obvious where the variable comes from. + +If we declare a variable with the same name, it takes precedence: ```html run untrusted height=0
@@ -36,8 +39,6 @@ That's unless we declare the same-named variable by our own: ``` -The behavior is described [in the specification](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem), but it is supported mainly for compatibility. The browser tries to help us by mixing namespaces of JS and DOM. Good for very simple scripts, but there may be name conflicts. Also, when we look in JS and don't have HTML in view, it's not obvious where the variable comes from. - The better alternative is to use a special method `document.getElementById(id)`. For instance: @@ -68,104 +69,9 @@ If there are multiple elements with the same `id`, then the behavior of correspo The method `getElementById` that can be called only on `document` object. It looks for the given `id` in the whole document. ``` -## getElementsBy* - -There are also other methods to look for nodes: - -- `elem.getElementsByTagName(tag)` looks for elements with the given tag and returns the collection of them. The `tag` parameter can also be a star `"*"` for "any tags". - -For instance: -```js -// get all divs in the document -let divs = document.getElementsByTagName('div'); -``` - -This method is callable in the context of any DOM element. - -Let's find all `input` tags inside the table: - -```html run height=50 - - - - - - -
Your age: - - - -
- - -``` - -```warn header="Don't forget the `\"s\"` letter!" -Novice developers sometimes forget the letter `"s"`. That is, they try to call `getElementByTagName` instead of getElementsByTagName. - -The `"s"` letter is absent in `getElementById`, because it returns a single element. But `getElementsByTagName` returns a collection of elements, so there's `"s"` inside. -``` - -````warn header="It returns a collection, not an element!" -Another widespread novice mistake is to write: - -```js -// doesn't work -document.getElementsByTagName('input').value = 5; -``` - -That won't work, because it takes a *collection* of inputs and assigns the value to it rather than to elements inside it. - -We should either iterate over the collection or get an element by its index, and then assign, like this: - -```js -// should work (if there's an input) -document.getElementsByTagName('input')[0].value = 5; -``` -```` - -There are also other rarely used methods of this kind: - -- `elem.getElementsByClassName(className)` returns elements that have the given CSS class. Elements may have other classes too. -- `document.getElementsByName(name)` returns elements with the given `name` attribute, document-wide. Exists for historical reasons, very rarely used, we mention it here only for completeness. - -For instance: - -```html run height=50 -
-
Article
-
Long article
-
- - -``` - ## querySelectorAll [#querySelectorAll] -Now goes the heavy artillery. - -The call to `elem.querySelectorAll(css)` returns all elements inside `elem` matching the given CSS selector. That's the most often used and powerful method. +By far, the most versatile method, `elem.querySelectorAll(css)` returns all elements inside `elem` matching the given CSS selector. Here we look for all `
  • ` elements that are last children: @@ -195,7 +101,6 @@ This method is indeed powerful, because any CSS selector can be used. Pseudo-classes in the CSS selector like `:hover` and `:active` are also supported. For instance, `document.querySelectorAll(':hover')` will return the collection with elements that the pointer is over now (in nesting order: from the outermost `` to the most nested one). ``` - ## querySelector [#querySelector] The call to `elem.querySelector(css)` returns the first element for the given CSS selector. @@ -230,9 +135,7 @@ For instance: ## closest -All elements that are directly above the given one are called its *ancestors*. - -In other words, ancestors are: parent, the parent of parent, its parent and so on. The ancestors together form the chain of parents from the element to the top. +*Ancestors* of an element are: parent, the parent of parent, its parent and so on. The ancestors together form the chain of parents from the element to the top. The method `elem.closest(css)` looks the nearest ancestor that matches the CSS-selector. The `elem` itself is also included in the search. @@ -260,14 +163,106 @@ For instance: ``` +## getElementsBy* + +There are also other methods to look for nodes by a tag, class, etc. + +Today, they are mostly history, as `querySelector` is more powerful and shorter to write. + +So here we cover them mainly for completeness, while you can still find them in the old scripts. + +- `elem.getElementsByTagName(tag)` looks for elements with the given tag and returns the collection of them. The `tag` parameter can also be a star `"*"` for "any tags". +- `elem.getElementsByClassName(className)` returns elements that have the given CSS class. Elements may have other classes too. +- `document.getElementsByName(name)` returns elements with the given `name` attribute, document-wide. very rarely used. + +For instance: +```js +// get all divs in the document +let divs = document.getElementsByTagName('div'); +``` + +Let's find all `input` tags inside the table: + +```html run height=50 + + + + + + +
    Your age: + + + +
    + + +``` + +```warn header="Don't forget the `\"s\"` letter!" +Novice developers sometimes forget the letter `"s"`. That is, they try to call `getElementByTagName` instead of getElementsByTagName. + +The `"s"` letter is absent in `getElementById`, because it returns a single element. But `getElementsByTagName` returns a collection of elements, so there's `"s"` inside. +``` + +````warn header="It returns a collection, not an element!" +Another widespread novice mistake is to write: + +```js +// doesn't work +document.getElementsByTagName('input').value = 5; +``` + +That won't work, because it takes a *collection* of inputs and assigns the value to it rather than to elements inside it. + +We should either iterate over the collection or get an element by its index, and then assign, like this: + +```js +// should work (if there's an input) +document.getElementsByTagName('input')[0].value = 5; +``` +```` + +Looking for `.article` elements: + +```html run height=50 +
    +
    Article
    +
    Long article
    +
    + + +``` + ## Live collections All methods `"getElementsBy*"` return a *live* collection. Such collections always reflect the current state of the document and "auto-update" when it changes. In the example below, there are two scripts. -1. The first one creates a reference to the collection of `
    `. As of now, it's length is `1`. -2. The second scripts runs after the browser meets one more `
    `, so it's length is `2`. +1. The first one creates a reference to the collection of `
    `. As of now, its length is `1`. +2. The second scripts runs after the browser meets one more `
    `, so its length is `2`. ```html run
    First div
    @@ -327,6 +322,18 @@ There are 6 main methods to search for nodes in DOM: +querySelector +CSS-selector +✔ +- + + +querySelectorAll +CSS-selector +✔ +- + + getElementById id - @@ -350,24 +357,10 @@ There are 6 main methods to search for nodes in DOM: ✔ ✔ - -querySelector -CSS-selector -✔ -- - - -querySelectorAll -CSS-selector -✔ -- - -Please note that methods `getElementById` and `getElementsByName` can only be called in the context of the document: `document.getElementById(...)`. But not on an element: `elem.getElementById(...)` would cause an error. - -Other methods can be called on elements too. For instance `elem.querySelectorAll(...)` will search inside `elem` (in the DOM subtree). +By far the most used are `querySelector` and `querySelectorAll`, but `getElementBy*` can be sporadically helpful or found in the old scripts. Besides that: diff --git a/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.png b/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.png index e48064cce..d82e90376 100644 Binary files a/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.png and b/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.png differ diff --git a/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy@2x.png b/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy@2x.png index 343f4890d..8a5ce5c0b 100644 Binary files a/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy@2x.png and b/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy@2x.png differ diff --git a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md index f7db9a042..b0a8ab7b1 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md @@ -7,7 +7,7 @@ importance: 3 Make all external links orange by altering their `style` property. A link is external if: -- It's `href` has `://` in it +- Its `href` has `://` in it - But doesn't start with `http://internal.com`. Example: diff --git a/2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md b/2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md index 70bc2ae9d..a1b53e337 100644 --- a/2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md +++ b/2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md @@ -8,4 +8,4 @@ Create a colored clock like here: [iframe src="solution" height=60] -Use HTML/CSS for the styling, Javascript only updates time in elements. +Use HTML/CSS for the styling, JavaScript only updates time in elements. diff --git a/2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html b/2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html index 9ba40a6d0..fd85e6c76 100644 --- a/2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html +++ b/2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html @@ -29,11 +29,12 @@ function createTreeText(obj) { // standalone recursive function let li = ''; + let ul; for (let key in obj) { li += '
  • ' + key + createTreeText(obj[key]) + '
  • '; } if (li) { - let ul = '
      ' + li + '
    ' + ul = '
      ' + li + '
    ' } return ul || ''; } diff --git a/2-ui/1-document/07-modifying-document/article.md b/2-ui/1-document/07-modifying-document/article.md index 9c38b3344..16aacb456 100644 --- a/2-ui/1-document/07-modifying-document/article.md +++ b/2-ui/1-document/07-modifying-document/article.md @@ -136,7 +136,7 @@ Here's a brief list of methods to insert a node into a parent element (`parentEl ``` To insert `newLi` as the first element, we can do it like this: - + ```js list.insertBefore(newLi, list.firstChild); ``` @@ -335,13 +335,81 @@ An example of copying the message: ``` + +## DocumentFragment [#document-fragment] + +`DocumentFragment` is a special DOM node that serves as a wrapper to pass around groups of nodes. + +We can append other nodes to it, but when we insert it somewhere, then it "disappears", leaving its content inserted instead. + +For example, `getListContent` below generates a fragment with `
  • ` items, that are later inserted into `
      `: + +```html run +
        + + +``` + +Please note, at the last line `(*)` we append `DocumentFragment`, but it "blends in", so the resulting structure will be: + +```html +
          +
        • 1
        • +
        • 2
        • +
        • 3
        • +
        +``` + +`DocumentFragment` is rarely used explicitly. Why append to a special kind of node, if we can return an array of nodes instead? Rewritten example: + +```html run +
          + + +``` + +We mention `DocumentFragment` mainly because there are some concepts on top of it, like [template](info:template-element) element, that we'll cover much later. + + ## Removal methods To remove nodes, there are the following methods: `parentElem.removeChild(node)` -: Removes `elem` from `parentElem` (assuming it's a child). +: Removes `node` from `parentElem` (assuming it's a child). `node.remove()` : Removes the `node` from its place. diff --git a/2-ui/1-document/07-modifying-document/before-prepend-append-after.png b/2-ui/1-document/07-modifying-document/before-prepend-append-after.png index 5bff84d85..858056f72 100644 Binary files a/2-ui/1-document/07-modifying-document/before-prepend-append-after.png and b/2-ui/1-document/07-modifying-document/before-prepend-append-after.png differ diff --git a/2-ui/1-document/07-modifying-document/before-prepend-append-after@2x.png b/2-ui/1-document/07-modifying-document/before-prepend-append-after@2x.png index 44c369ee6..9d6894314 100644 Binary files a/2-ui/1-document/07-modifying-document/before-prepend-append-after@2x.png and b/2-ui/1-document/07-modifying-document/before-prepend-append-after@2x.png differ diff --git a/2-ui/1-document/07-modifying-document/insert-adjacent.png b/2-ui/1-document/07-modifying-document/insert-adjacent.png index 08063bc51..7cf2f5973 100644 Binary files a/2-ui/1-document/07-modifying-document/insert-adjacent.png and b/2-ui/1-document/07-modifying-document/insert-adjacent.png differ diff --git a/2-ui/1-document/07-modifying-document/insert-adjacent@2x.png b/2-ui/1-document/07-modifying-document/insert-adjacent@2x.png index 60333ad1b..fcb14017a 100644 Binary files a/2-ui/1-document/07-modifying-document/insert-adjacent@2x.png and b/2-ui/1-document/07-modifying-document/insert-adjacent@2x.png differ diff --git a/2-ui/1-document/08-styles-and-classes/article.md b/2-ui/1-document/08-styles-and-classes/article.md index ed9db3032..5159c532d 100644 --- a/2-ui/1-document/08-styles-and-classes/article.md +++ b/2-ui/1-document/08-styles-and-classes/article.md @@ -116,7 +116,7 @@ Sometimes we want to assign a style property, and later remove it. For instance, to hide an element, we can set `elem.style.display = "none"`. -Then later we may want to remove the `style.display` as if it were not set. Instead of `delete elem.style.display` we should assign an empty line to it: `elem.style.display = ""`. +Then later we may want to remove the `style.display` as if it were not set. Instead of `delete elem.style.display` we should assign an empty string to it: `elem.style.display = ""`. ```js run // if we run this code, the "blinks" @@ -207,7 +207,7 @@ For instance, here `style` doesn't see the margin: ``` -...But what if we need, say, to increase the margin by 20px? We want the current value for the start. +...But what if we need, say, to increase the margin by 20px? We would want the current value of it. There's another method for that: `getComputedStyle`. @@ -281,7 +281,7 @@ Visited links may be colored using `:visited` CSS pseudoclass. But `getComputedStyle` does not give access to that color, because otherwise an arbitrary page could find out whether the user visited a link by creating it on the page and checking the styles. -JavaScript we may not see the styles applied by `:visited`. And also, there's a limitation in CSS that forbids to apply geometry-changing styles in `:visited`. That's to guarantee that there's no sideway for an evil page to test if a link was visited and hence to break the privacy. +JavaScript may not see the styles applied by `:visited`. And also, there's a limitation in CSS that forbids to apply geometry-changing styles in `:visited`. That's to guarantee that there's no sideway for an evil page to test if a link was visited and hence to break the privacy. ``` ## Summary diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.png b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.png index 098f9eb6d..67b9019a0 100644 Binary files a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.png and b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.png differ diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field@2x.png b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field@2x.png index 3ecb29375..fe58f3542 100644 Binary files a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field@2x.png and b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field@2x.png differ diff --git a/2-ui/1-document/09-size-and-scroll/article.md b/2-ui/1-document/09-size-and-scroll/article.md index 952dd7275..774857f3f 100644 --- a/2-ui/1-document/09-size-and-scroll/article.md +++ b/2-ui/1-document/09-size-and-scroll/article.md @@ -64,7 +64,7 @@ The `offsetParent` is the nearest ancestor that is: 2. or ``, ``, ``, 2. or ``. -In most practical cases we can use `offsetParent` to get the nearest CSS-positioned ancestor. And `offsetLeft/offsetTop` provide x/y coordinates relative to it's upper-left corner. +In most practical cases we can use `offsetParent` to get the nearest CSS-positioned ancestor. And `offsetLeft/offsetTop` provide x/y coordinates relative to its upper-left corner. In the example below the inner `
          ` has `
          ` as `offsetParent` and `offsetLeft/offsetTop` shifts from its upper-left corner (`180`): diff --git a/2-ui/1-document/09-size-and-scroll/metric-all.png b/2-ui/1-document/09-size-and-scroll/metric-all.png index a6335bcd8..6060b9890 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-all.png and b/2-ui/1-document/09-size-and-scroll/metric-all.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-all@2x.png b/2-ui/1-document/09-size-and-scroll/metric-all@2x.png index e084c257b..3c53be1dd 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-all@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-all@2x.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.png b/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.png index ffb10aa1b..efcc9440b 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.png and b/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl@2x.png b/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl@2x.png index 48510e6e0..9b7edcf57 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl@2x.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top.png b/2-ui/1-document/09-size-and-scroll/metric-client-left-top.png index 33faf9be7..787557205 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-left-top.png and b/2-ui/1-document/09-size-and-scroll/metric-client-left-top.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top@2x.png b/2-ui/1-document/09-size-and-scroll/metric-client-left-top@2x.png index a8e170eab..8034c7aff 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-left-top@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-client-left-top@2x.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-height.png b/2-ui/1-document/09-size-and-scroll/metric-client-width-height.png index d10edb5f6..d50d1186f 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-width-height.png and b/2-ui/1-document/09-size-and-scroll/metric-client-width-height.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-height@2x.png b/2-ui/1-document/09-size-and-scroll/metric-client-width-height@2x.png index f31f483e4..76017b9ee 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-width-height@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-client-width-height@2x.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.png b/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.png index 8280e40f1..173bf1529 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.png and b/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding@2x.png b/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding@2x.png index 5a73f87a9..2d145d77f 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding@2x.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-css.png b/2-ui/1-document/09-size-and-scroll/metric-css.png index a28bbab78..5579b4abd 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-css.png and b/2-ui/1-document/09-size-and-scroll/metric-css.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-css@2x.png b/2-ui/1-document/09-size-and-scroll/metric-css@2x.png index e4fd7bb87..c4df7706b 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-css@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-css@2x.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png b/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png index a0fba50d2..059ef13ef 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png and b/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png b/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png index 125a91e15..0f86b4f85 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.png b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.png index f25957848..82551b8e1 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.png and b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height@2x.png b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height@2x.png index 7bfb24c22..805687cd6 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height@2x.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-top.png b/2-ui/1-document/09-size-and-scroll/metric-scroll-top.png index 13c57161f..83f410837 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-scroll-top.png and b/2-ui/1-document/09-size-and-scroll/metric-scroll-top.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-top@2x.png b/2-ui/1-document/09-size-and-scroll/metric-scroll-top@2x.png index bf0cf711a..1ef15502e 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-scroll-top@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-scroll-top@2x.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.png b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.png index 197a5d764..54e6b9634 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.png and b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height@2x.png b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height@2x.png index 1e2a20f54..d57c5c198 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height@2x.png differ diff --git a/2-ui/1-document/10-size-and-scroll-window/article.md b/2-ui/1-document/10-size-and-scroll-window/article.md index 4a62f56c6..b8c16a3c7 100644 --- a/2-ui/1-document/10-size-and-scroll-window/article.md +++ b/2-ui/1-document/10-size-and-scroll-window/article.md @@ -1,6 +1,6 @@ # Window sizes and scrolling -How to find out the width of the browser window? How to get the full height of the document, including the scrolled out part? How to scroll the page using JavaScript? +How to find out the width and height of the browser window? How to get the full width and height of the document, including the scrolled out part? How to scroll the page using JavaScript? From the DOM point of view, the root document element is `document.documentElement`. That element corresponds to `` and has geometry properties described in the [previous chapter](info:size-and-scroll). For some cases we can use it, but there are additional methods and peculiarities important enough to consider. @@ -44,7 +44,7 @@ Theoretically, as the root document element is `documentElement.clientWidth/Heig These properties work well for regular elements. But for the whole page these properties do not work as intended. In Chrome/Safari/Opera if there's no scroll, then `documentElement.scrollHeight` may be even less than `documentElement.clientHeight`! For regular elements that's a nonsense. -To have a reliable full window size, we should take the maximum of these properties: +To have a reliable result on the full document height, we should take the maximum of these properties: ```js run let scrollHeight = Math.max( @@ -96,7 +96,7 @@ It should work, but smells like cross-browser incompatibilities. Not good. Fortu ``` -- The method `scrollTo(pageX,pageY)` scrolls the page relative to the document top-left corner. It's like setting `scrollLeft/scrollTop`. +- The method `scrollTo(pageX,pageY)` scrolls the page relative to the document's top-left corner. It's like setting `scrollLeft/scrollTop`. To scroll to the very beginning, we can use `scrollTo(0,0)`. @@ -129,7 +129,7 @@ And this button scrolls the page to show it at the bottom: Sometimes we need to make the document "unscrollable". For instance, when we need to cover it with a large message requiring immediate attention, and we want the visitor to interact with that message, not with the document. -To make the document unscrollable, its enough to set `document.body.style.overflow = "hidden"`. The page will freeze on its current scroll. +To make the document unscrollable, it's enough to set `document.body.style.overflow = "hidden"`. The page will freeze on its current scroll. ```online Try it: @@ -145,7 +145,7 @@ We can use the same technique to "freeze" the scroll for other elements, not jus The drawback of the method is that the scrollbar disappears. If it occupied some space, then that space is now free, and the content "jumps" to fill it. -That looks a bit odd, but can be worked around if we compare `clientWidth` before and after the freeze, and if it increased (the scrollbar disappeared) then add `padding` to `document.body` in place of the scrollbar, to keep the content width same. +That looks a bit odd, but can be worked around if we compare `clientWidth` before and after the freeze, and if it increased (the scrollbar disappeared) then add `padding` to `document.body` in place of the scrollbar, to keep the content width the same. ## Summary diff --git a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.png b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.png index 76a45a7a7..a3cf95f11 100644 Binary files a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.png and b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.png differ diff --git a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height@2x.png b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height@2x.png index 249db0edb..868dc8a26 100644 Binary files a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height@2x.png and b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height@2x.png differ diff --git a/2-ui/1-document/11-coordinates/article.md b/2-ui/1-document/11-coordinates/article.md index ac5978f12..61b5161eb 100644 --- a/2-ui/1-document/11-coordinates/article.md +++ b/2-ui/1-document/11-coordinates/article.md @@ -48,7 +48,7 @@ Also: - Coordinates may be decimal fractions. That's normal, internally browser uses them for calculations. We don't have to round them when setting to `style.position.left/top`, the browser is fine with fractions. - Coordinates may be negative. For instance, if the page is scrolled down and the top `elem` is now above the window. Then, `elem.getBoundingClientRect().top` is negative. -- Some browsers (like Chrome) provide additional properties (`width` and `height`) to `getBoundingClientRect` as the result. We can also get them by subtraction: `height=bottom-top`, `width=right-left`. +- Some browsers (like Chrome) provide additional properties, `width` and `height` of the element that invoked the method to `getBoundingClientRect` as the result. We can also get them by subtraction: `height=bottom-top`, `width=right-left`. ```warn header="Coordinates right/bottom are different from CSS properties" If we compare window coordinates versus CSS positioning, then there are obvious similarities to `position:fixed`. The positioning of an element is also relative to the viewport. diff --git a/2-ui/1-document/11-coordinates/coords.png b/2-ui/1-document/11-coordinates/coords.png index da4869fb9..79d1fdbb6 100644 Binary files a/2-ui/1-document/11-coordinates/coords.png and b/2-ui/1-document/11-coordinates/coords.png differ diff --git a/2-ui/1-document/11-coordinates/coords@2x.png b/2-ui/1-document/11-coordinates/coords@2x.png index c3469f4cd..d3e340f50 100644 Binary files a/2-ui/1-document/11-coordinates/coords@2x.png and b/2-ui/1-document/11-coordinates/coords@2x.png differ diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png b/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png index 9e4d9d5e9..29c59aad1 100644 Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png and b/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png differ diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png b/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png index 52e555c49..3c188ee45 100644 Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png and b/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png differ diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png b/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png index 3624196b0..3dc17f025 100644 Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png and b/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png differ diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png b/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png index 83f31950c..45f6b9e7f 100644 Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png and b/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png differ diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.png b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.png index ca2f49edb..4a0549582 100644 Binary files a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.png and b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.png differ diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords@2x.png b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords@2x.png index b4c4d3ef2..ecf4e5d2f 100644 Binary files a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords@2x.png and b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords@2x.png differ diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md index 8312f0391..bda48e2bb 100644 --- a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md @@ -33,7 +33,7 @@ We have `event.clientX/clientY` -- window-relative coordinates of the click. To get field-relative `left` coordinate of the click, we can substract the field left edge and the border width: ```js -let left = event.clientX - fieldInnerCoords.left - field.clientLeft; +let left = event.clientX - fieldCoords.left - field.clientLeft; ``` Normally, `ball.style.position.left` means the "left edge of the element" (the ball). So if we assign that `left`, then the ball edge would be under the mouse cursor. @@ -43,7 +43,7 @@ We need to move the ball half-width left and half-height up to make it center. So the final `left` would be: ```js -let left = event.clientX - fieldInnerCoords.left - field.clientLeft - ball.offsetWidth/2; +let left = event.clientX - fieldCoords.left - field.clientLeft - ball.offsetWidth/2; ``` The vertical coordinate is calculated using the same logic. diff --git a/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md b/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md index f4c105941..eed096231 100644 --- a/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md @@ -2,9 +2,9 @@ # HTML/CSS First let's create HTML/CSS. -A menu is a standalone graphical component on the page, so its better to put it into a single DOM element. +A menu is a standalone graphical component on the page, so it's better to put it into a single DOM element. -A list of menu items can be layed out as a list `ul/li`. +A list of menu items can be laid out as a list `ul/li`. Here's the example structure: diff --git a/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/task.md b/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/task.md index 05af13cca..34c313710 100644 --- a/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/task.md +++ b/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/task.md @@ -2,7 +2,7 @@ importance: 5 --- -# Create a menu sliding menu +# Create a sliding menu Create a menu that opens/collapses on click: diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.png b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.png index b58ac0a66..93defb0bb 100644 Binary files a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.png and b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.png differ diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1@2x.png b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1@2x.png index fe6a0badd..7aa0d0f84 100644 Binary files a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1@2x.png and b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1@2x.png differ diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.png b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.png index 351ead9c4..a61bf5e57 100644 Binary files a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.png and b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.png differ diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2@2x.png b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2@2x.png index 05dbc1adf..4742579db 100644 Binary files a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2@2x.png and b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2@2x.png differ diff --git a/2-ui/2-events/01-introduction-browser-events/article.md b/2-ui/2-events/01-introduction-browser-events/article.md index dcf64a200..332d15793 100644 --- a/2-ui/2-events/01-introduction-browser-events/article.md +++ b/2-ui/2-events/01-introduction-browser-events/article.md @@ -18,7 +18,7 @@ Here's a list of the most useful DOM events, just to take a look at: **Keyboard events:** - `keydown` and `keyup` -- when the visitor presses and then releases the button. -**Document events** +**Document events:** - `DOMContentLoaded` -- when the HTML is loaded and processed, DOM is fully built. **CSS events:** @@ -216,7 +216,7 @@ Web-standard developers understood that long ago and suggested an alternative wa The syntax to add a handler: ```js -element.addEventListener(event, handler[, phase]); +element.addEventListener(event, handler[, options]); ``` `event` @@ -225,15 +225,17 @@ element.addEventListener(event, handler[, phase]); `handler` : The handler function. -`phase` -: An optional argument, the "phase" for the handler to work. To be covered later. Usually we don't use it. +`options` +: An additional optional object with properties: + - `once`: if `true`, then the listener is automatically removed after it triggers. + - `capture`: the phrase where to handle the event, to be covered later in the chapter . For historical reasons, `options` can also be `false/true`, that's the same as `{capture: false/true}`. + - `passive`: if `true`, then the handler will not `preventDefault()`, we'll cover that later in . -To remove the handler, use `removeEventListener`: +To remove the handler, use `removeEventListener`: ```js -// exactly the same arguments as addEventListener -element.removeEventListener(event, handler[, phase]); +element.removeEventListener(event, handler[, options]); ``` ````warn header="Removal requires the same function" diff --git a/2-ui/2-events/02-bubbling-and-capturing/article.md b/2-ui/2-events/02-bubbling-and-capturing/article.md index 287f74f2a..82ec8a84a 100644 --- a/2-ui/2-events/02-bubbling-and-capturing/article.md +++ b/2-ui/2-events/02-bubbling-and-capturing/article.md @@ -108,7 +108,7 @@ Sometimes `event.stopPropagation()` creates hidden pitfalls that later may becom For instance: -1. We create a nested menu. Each submenu handles clicks on its elements and calls `stopPropagation` so that outer menu don't trigger. +1. We create a nested menu. Each submenu handles clicks on its elements and calls `stopPropagation` so that the outer menu won't trigger. 2. Later we decide to catch clicks on the whole window, to track users' behavior (where people click). Some analytic systems do that. Usually the code uses `document.addEventListener('click'…)` to catch all clicks. 3. Our analytic won't work over the area where clicks are stopped by `stopPropagation`. We've got a "dead zone". @@ -136,9 +136,15 @@ That is: for a click on `
          ` the event first goes through the ancestors chain Handlers added using `on`-property or using HTML attributes or using `addEventListener(event, handler)` don't know anything about capturing, they only run on the 2nd and 3rd phases. -To catch an event on the capturing phase, we need to set the 3rd argument of `addEventListener` to `true`. +To catch an event on the capturing phase, we need to set the handler `capture` option to `true`: -There are two possible values for that optional last argument: +```js +elem.addEventListener(..., {capture: true}) +// or, just "true" is an alias to {capture: true} +elem.addEventListener(..., true) +``` + +There are two possible values of the `capture` option: - If it's `false` (default), then the handler is set on the bubbling phase. - If it's `true`, then the handler is set on the capturing phase. @@ -182,12 +188,16 @@ Please note that `P` shows up two times: at the end of capturing and at the star There's a property `event.eventPhase` that tells us the number of the phase on which the event was caught. But it's rarely used, because we usually know it in the handler. +```smart header="To remove the handler, `removeEventListener` needs the same phase" +If we `addEventListener(..., true)`, then we should mention the same phase in `removeEventListener(..., true)` to correctly remove the handler. +``` + ## Summary The event handling process: - When an event happens -- the most nested element where it happens gets labeled as the "target element" (`event.target`). -- Then the event first moves from the document root down to the `event.target`, calling handlers assigned with `addEventListener(...., true)` on the way. +- Then the event first moves from the document root down to the `event.target`, calling handlers assigned with `addEventListener(...., true)` on the way (`true` is a shorthand for `{capture: true}`). - Then the event moves from `event.target` up to the root, calling handlers assigned using `on` and `addEventListener` without the 3rd argument or with the 3rd argument `false`. Each handler can access `event` object properties: diff --git a/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.png b/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.png index a4f4fae51..9de1f2048 100644 Binary files a/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.png and b/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.png differ diff --git a/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling@2x.png b/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling@2x.png index e3a070cb8..81bb24c70 100644 Binary files a/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling@2x.png and b/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling@2x.png differ diff --git a/2-ui/2-events/02-bubbling-and-capturing/eventflow.png b/2-ui/2-events/02-bubbling-and-capturing/eventflow.png index 95c5c08e0..56fa230a6 100644 Binary files a/2-ui/2-events/02-bubbling-and-capturing/eventflow.png and b/2-ui/2-events/02-bubbling-and-capturing/eventflow.png differ diff --git a/2-ui/2-events/02-bubbling-and-capturing/eventflow@2x.png b/2-ui/2-events/02-bubbling-and-capturing/eventflow@2x.png index d52c4f70c..17ab1bf8e 100644 Binary files a/2-ui/2-events/02-bubbling-and-capturing/eventflow@2x.png and b/2-ui/2-events/02-bubbling-and-capturing/eventflow@2x.png differ diff --git a/2-ui/2-events/03-event-delegation/bagua-bubble.png b/2-ui/2-events/03-event-delegation/bagua-bubble.png index f0433aeb1..55adbd0c1 100644 Binary files a/2-ui/2-events/03-event-delegation/bagua-bubble.png and b/2-ui/2-events/03-event-delegation/bagua-bubble.png differ diff --git a/2-ui/2-events/03-event-delegation/bagua-bubble@2x.png b/2-ui/2-events/03-event-delegation/bagua-bubble@2x.png index 8cc5ca913..86b6d6807 100644 Binary files a/2-ui/2-events/03-event-delegation/bagua-bubble@2x.png and b/2-ui/2-events/03-event-delegation/bagua-bubble@2x.png differ diff --git a/2-ui/2-events/04-default-browser-action/article.md b/2-ui/2-events/04-default-browser-action/article.md index b2889132f..44355de0d 100644 --- a/2-ui/2-events/04-default-browser-action/article.md +++ b/2-ui/2-events/04-default-browser-action/article.md @@ -93,6 +93,20 @@ But if you click the second one, there's no focus. That's because the browser action is canceled on `mousedown`. The focusing is still possible if we use another way to enter the input. For instance, the `key:Tab` key to switch from the 1st input into the 2nd. But not with the mouse click any more. +## The "passive" handler option + +The optional `passive: true` option of `addEventListener` signals the browser that the handler is not going to call `preventDefault()`. + +Why that may be needed? + +There are some events like `touchmove` on mobile devices (when the user moves their finger across the screen), that cause scrolling by default, but that scrolling can be prevented using `preventDefault()` in the handler. + +So when the browser detects such event, it has first to process all handlers, and then if `preventDefault` is not called anywhere, it can proceed with scrolling. That may cause unnecessary delays and "jitters" in the UI. + +The `passive: true` options tells the browser that the handler is not going to cancel scrolling. Then browser scrolls immediately providing a maximally fluent experience, and the event is handled by the way. + +For some browsers (Firefox, Chrome), `passive` is `true` by default for `touchstart` and `touchmove` events. + ## event.defaultPrevented @@ -215,6 +229,8 @@ All the default actions can be prevented if we want to handle the event exclusiv To prevent a default action -- use either `event.preventDefault()` or `return false`. The second method works only for handlers assigned with `on`. +The `passive: true` option of `addEventListener` tells the browser that the action is not going to be prevented. That's useful for some mobile events, like `touchstart` and `touchmove`, to tell the browser that it should not wait for all handlers to finish before scrolling. + If the default action was prevented, the value of `event.defaultPrevented` becomes `true`, otherwise it's `false`. ```warn header="Stay semantic, don't abuse" diff --git a/2-ui/2-events/05-dispatch-events/article.md b/2-ui/2-events/05-dispatch-events/article.md index 8b37edae7..2b13d2360 100644 --- a/2-ui/2-events/05-dispatch-events/article.md +++ b/2-ui/2-events/05-dispatch-events/article.md @@ -148,7 +148,7 @@ For instance: *!* detail: { name: "John" } */!* - }); + })); ``` diff --git a/2-ui/2-events/index.md b/2-ui/2-events/index.md index 07f06fdda..f4996083c 100644 --- a/2-ui/2-events/index.md +++ b/2-ui/2-events/index.md @@ -1,3 +1,3 @@ -# Introduction into Events +# Introduction to Events An introduction to browser events, event properties and handling patterns. diff --git a/2-ui/3-event-details/11-onload-onerror/article.md b/2-ui/3-event-details/11-onload-onerror/article.md deleted file mode 100644 index 7792626c7..000000000 --- a/2-ui/3-event-details/11-onload-onerror/article.md +++ /dev/null @@ -1,91 +0,0 @@ -# Resource loading: onload and onerror - -The browser allows to track the loading of external resources -- scripts, iframes, pictures and so on. - -There are two events for it: - -- `onload` -- successful load, -- `onerror` -- an error occurred. - -## Loading a script - -Let's say we need to call a function that resides in an external script. - -We can load it dynamically, like this: - -```js -let script = document.createElement('script'); -script.src = "my.js"; - -document.head.append(script); -``` - -...But how to run the function that is declared inside that script? We need to wait until the script loads, and only then we can call it. - -### script.onload - -The main helper is the `load` event. It triggers after the script was loaded and executed. - -For instance: - -```js run untrusted -let script = document.createElement('script'); - -// can load any script, from any domain -script.src = "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js" -document.head.append(script); - -*!* -script.onload = function() { - // the script creates a helper function "_" - alert(_); // the function is available -}; -*/!* -``` - -So in `onload` we can use script variables, run functions etc. - -...And what if the loading failed? For instance, there's no such script (error 404) or the server or the server is down (unavailable). - -### script.onerror - -Errors that occur during the loading (but not execution) of the script can be tracked on `error` event. - -For instance, let's request a script that doesn't exist: - -```js run -let script = document.createElement('script'); -script.src = "https://example.com/404.js"; // no such script -document.head.append(script); - -*!* -script.onerror = function() { - alert("Error loading " + this.src); // Error loading https://example.com/404.js -}; -*/!* -``` - -Please note that we can't get error details here. We don't know was it error 404 or 500 or something else. Just that the loading failed. - -## Other resources - -The `load` and `error` events also work for other resources. There may be minor differences though. - -For instance: - -``, `` (external stylesheets) -: Both `load` and `error` events work as expected. - -` + + +
          + ... +
          + +``` + +- So, it was possible to make a GET/POST request to another site, even without networking methods. +- But as it's forbidden to access the content of an ` + + diff --git a/6-data-storage/03-indexeddb/article.md b/6-data-storage/03-indexeddb/article.md new file mode 100644 index 000000000..d7fc5bbbd --- /dev/null +++ b/6-data-storage/03-indexeddb/article.md @@ -0,0 +1,754 @@ +libs: + - 'https://cdn.jsdelivr.net/npm/idb@3.0.2/build/idb.min.js' + +--- + +# IndexedDB + +IndexedDB is a built-in database, much more powerful than `localStorage`. + +- Key/value storage: value can be (almost) anything, multiple key types. +- Supports transactions for reliability. +- Supports key range queries, indexes. +- Can store much more data than `localStorage`. + +That power is usually excessive for traditional client-server apps. IndexedDB is intended for offline apps, to be combined with ServiceWorkers and other technologies. + +The native interface to IndexedDB, described in the specification , is event-based. + +We can also use `async/await` with the help of a promise-based wrapper, like . That's pretty convenient, but the wrapper is not perfect, it can't replace events for all cases, so we'll start with events, and then use the wrapper. + +## Open database + +To start working with IndexedDB, we need to open a database. + +The syntax: + +```js +let openRequest = indexedDB.open(name, version); +``` + +- `name` -- a string, the database name. +- `version` -- a positive integer version, by default `1` (explained below). + +We can have many databases with different names, all within the current origin (domain/protocol/port). So different websites can't access databases of each other. + +After the call, we need to listen to events on `openRequest` object: +- `success`: database is ready, use the database object `openRequest.result` for further work. +- `error`: open failed. +- `upgradeneeded`: database version is outdated (see below). + +**IndexedDB has a built-in mechanism of "schema versioning", absent in server-side databases.** + +Unlike server-side databases, IndexedDB is client-side, we don't have the data at hands. But when we publish a new version of our app, we may need to update the database. + +If the local database version is less than specified in `open`, then a special event `upgradeneeded` is triggered, and we can compare versions and upgrade data structures as needed. + +The event also triggers when the database did not exist yet, so we can perform initialization. + +For instance, when we first publish our app, we open it with version `1` and perform the initialization in `upgradeneeded` handler: + +```js +let openRequest = indexedDB.open("store", *!*1*/!*); + +openRequest.onupgradeneeded = function() { + // triggers if the client had no database + // ...perform initialization... +}; + +openRequest.onerror = function() { + console.error("Error", openResult.error); +}; + +openRequest.onsuccess = function() { + let db = openRequest.result; + // continue to work with database using db object +}; +``` + +When we publish the 2nd version: + +```js +let openRequest = indexedDB.open("store", *!*2*/!*); + +// check the existing database version, do the updates if needed: +openRequest.onupgradeneeded = function() { + let db = openRequest.result; + switch(db.version) { // existing (old) db version + case 0: + // version 0 means that the client had no database + // perform initialization + case 1: + // client had version 1 + // update + } +}; +``` + +After `openRequest.onsuccess` we have the database object in `openRequest.result`, that we'll use for further operations. + +To delete a database: + +```js +let deleteRequest = indexedDB.deleteDatabase(name) +// deleteRequest.onsuccess/onerror tracks the result +``` + + +## Object store + +An object store is a core concept of IndexedDB. Counterparts in other databases are called "tables" or "collections". It's where the data is stored. A database may have multiple stores: one for users, another one for goods, etc. + +Despite being named an "object store", primitives can be stored too. + +**We can store almost any value, including complex objects.** + +IndexedDB uses the [standard serialization algorithm](https://www.w3.org/TR/html53/infrastructure.html#section-structuredserializeforstorage) to clone-and-store an object. It's like `JSON.stringify`, but more powerful, capable of storing much more datatypes. + +An example of object that can't be stored: an object with circular references. Such objects are not serializable. `JSON.stringify` also fails for such objects. + +**There must be an unique `key` for every value in the store.** + +A key must have a type one of: number, date, string, binary, or array. It's a unique object identifier: we can search/remove/update values by the key. + +![](indexeddb-structure.png) + +We can provide a key when we add an value to the store, similar to `localStorage`. That's good for storing primitive values. But when we store objects, IndexedDB allows to setup an object property as the key, that's much more convenient. Or we can auto-generate keys. + +The syntax to create an object store: +```js +db.createObjectStore(name[, keyOptions]); +``` + +Please note, the operation is synchronous, no `await` needed. + +- `name` is the store name, e.g. `"books"` for books, +- `keyOptions` is an optional object with one of two properties: + - `keyPath` -- a path to an object property that IndexedDB will use as the key, e.g. `id`. + - `autoIncrement` -- if `true`, then the key for a newly stored object is generated automatically, as an ever-incrementing number. + +If we don't supply any options, then we'll need to provide a key explicitly later, when storing an object. + +For instance, this object store uses `id` property as the key: +```js +db.createObjectStore('books', {keyPath: 'id'}); +``` + +**An object store can only be created/modified while updating the DB version, in `upgradeneeded` handler.** + +That's a technical limitation. Outside of the handler we'll be able to add/remove/update the data, but object stores are changed only during version update. + +To do an upgrade, there are two main ways: +1. We can compare versions and run per-version operations. +2. Or we can get a list of existing object stores as `db.objectStoreNames`. That object is a [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist), and it provides `contains(name)` method to check for the existance. And then we can do updates depending on what exists. + +Here's the demo of the second approach: + +```js +let openRequest = indexedDB.open("db", 1); + +// create an object store for books if not exists +openRequest.onupgradeneeded = function() { + let db = openRequest.result; + if (!db.objectStoreNames.contains('books')) { + db.createObjectStore('books', {keyPath: 'id'}); + } +}; +``` + + +To delete an object store: + +```js +db.deleteObjectStore('books') +``` + +## Transactions + +The term "transaction" is generic, used in many kinds of databases. + +A transaction is a group operations, that should either all succeed or all fail. + +For instance, when a person buys something, we need: +1. Subtract the money from their account. +2. Add the item to their inventory. + +It would be pretty bad if we complete the 1st operation, and then something goes wrong, e.g. lights out, and we fail to do the 2nd. Both should either succeed (purchase complete, good!) or both fail (at least the person kept their money, so they can retry). + +Transactions can guarantee that. + +**All data operations must be made within a transaction in IndexedDB.** + +To start a transaction: + +```js run +db.transaction(store[, type]); +``` + +- `store` is a store name that the transaction is going to access, e.g. `"books"`. Can be an array of store names if we're going to access multiple stores. +- `type` – a transaction type, one of: + - `readonly` -- can only read, the default. + - `readwrite` -- can only read and write, but not modify object stores. + +There'is also `versionchange` transaction type: such transactions can do everything, but we can't create them manually. IndexedDB automatically creates a `versionchange` transaction when opening the database, for `updateneeded` handler. That's why it's a single place where we can update the database structure, create/remove object stores. + +```smart header="What are transaction types for?" +Performance is the reason why transactions need to be labeled either `readonly` and `readwrite`. + +Many `readonly` transactions can access concurrently the same store, but `readwrite` transactions can't. A `readwrite` transaction "locks" the store for writing. The next transaction must wait before the previous one finishes before accessing the same store. +``` + +After the transaction is created, we can add an item to the store, like this: + +```js +let transaction = db.transaction("books", "readwrite"); // (1) + +// get an object store to operate on it +*!* +let books = transaction.objectStore("books"); // (2) +*/!* + +let book = { + id: 'js', + price: 10, + created: new Date() +}; + +*!* +let request = books.add(book); // (3) +*/!* + +request.onsuccess = function() { // (4) + console.log("Book added to the store", request.result); +}; + +request.onerror = function() { + console.log("Error", request.error); +}; +``` + +There are basically four steps: + +1. Create a transaction, mention all stores it's going to access, at `(1)`. +2. Get the store object using `transaction.objectStore(name)`, at `(2)`. +3. Perform the request to the object store `books.add(book)`, at `(3)`. +4. ...Handle request success/error `(4)`, make other requests if needed, etc. + +Object stores support two methods to store a value: + +- **put(value, [key])** + Add the `value` to the store. The `key` is supplied only if the object store did not have `keyPath` or `autoIncrement` option. If there's already a value with same key, it will be replaced. + +- **add(value, [key])** + Same as `put`, but if there's already a value with the same key, then the request fails, and an error with the name `"ConstraintError"` is generated. + +Just like when opening a database, we send a request: `books.add(book)`, and then wait for `success/error` events. + +- The `request.result` for `add` is the key of the new object. +- The error is in `request.error` (if any). + +## Transactions autocommit + +In the example above we started the transaction and made `add` request. We could make more requests. How do we finish ("commit") the transaction? + +The short answer is: we don't. + +In the next version 3.0 of the specification, there will probably be a manual way to finish the transaction, but right now in 2.0 there isn't. + +**When all transaction requests are finished, and the [microtasks queue](info:microtask-queue) is empty, it is committed automatically.** + +```smart header="What's an \"empty microtask queue\"?" +The microtask queue is explained in [another chapter](info:async-await#microtask-queue). In short, an empty microtask queue means that for all settled promises their `.then/catch/finally` handlers are executed. + +In other words, handling of finished promises and resuming "awaits" is done before closing the transaction. + +That's a minor technical detail. If we're using `async/await` instead of low-level promise calls, then we can assume that a transaction commits when all its requests are done, and the current code finishes. +``` + +So, in the example above no special code is needed to finish the transaction. + +Transactions auto-commit principle has an important side effect. We can't insert an async operation like `fetch`, `setTimeout` in the middle of transaction. IndexedDB will not keep the transaction waiting till these are done. + +In the code below `request2` in line `(*)` fails, because the transaction is already committed, can't make any request in it: + +```js +let request1 = books.add(book); + +request1.onsuccess = function() { + fetch('/').then(response => { +*!* + let request2 = books.add(anotherBook); // (*) +*/!* + request2.onerror = function() { + console.log(request2.error.name); // TransactionInactiveError + }; + }); +}; +``` + +That's because `fetch` is an asynchronous operation, a macrotask. Transactions are closed before the browser starts doing macrotasks. + +Authors of IndexedDB spec believe that transactions should be short-lived. Mostly for performance reasons. + +Notably, `readwrite` transactions "lock" the stores for writing. So if one part of application initiated `readwrite` on `books` object store, then another part that wants to do the same has to wait: the new transaction "hangs" till the first one is done. That can lead to strange delays if transactions take a long time. + +So, what to do? + +In the example above we could make a new `db.transaction` right before the new request `(*)`. + +But it will be even better, if we'd like to keep the operations together, in one transaction, to split apart IndexedDB transactions and "other" async stuff. + +First, make `fetch`, prepare the data if needed, afterwards create a transaction and perform all the database requests, it'll work then. + +To detect the moment of successful completion, we can listen to `transaction.oncomplete` event: + +```js +let transaction = db.transaction("books", "readwrite"); + +// ...perform operations... + +transaction.oncomplete = function() { + console.log("Transaction is complete"); +}; +``` + +Only `complete` guarantees that the transaction is saved as a whole. Individual requests may succeed, but the final write operation may go wrong (e.g. I/O error or something). + +To manually abort the transaction, call: + +```js +transaction.abort(); +``` + +That cancels all modification made by the requests in it and triggers `transaction.onabort` event. + + +## Error handling + +Write requests may fail. + +That's to be expected, not only because of possible errors at our side, but also for reasons not related to the transaction itself. For instance, the storage quota may be exceeded. So we must be ready to handle such case. + +**A failed request automatically aborts the transaction, canceling all its changes.** + +Sometimes a request may fail with a non-critical error. We'd like to handle it in `request.onerror` and continue the transaction. Then, to prevent the transaction abort, we should call `event.preventDefault()`. + +In the example below a new book is added with the same key (`id`). The `store.add` method generates a `"ConstraintError"` in that case. We handle it without canceling the transaction: + +```js +let transaction = db.transaction("books", "readwrite"); + +let book = { id: 'js', price: 10 }; + +let request = transaction.objectStore("books").add(book); + +request.onerror = function(event) { + // ConstraintError occurs when an object with the same id already exists + if (request.error.name == "ConstraintError") { + console.log("Book with such id already exists"); // handle the error + event.preventDefault(); // don't abort the transaction + } else { + // unexpected error, can't handle it + // the transaction will abort + } +}; + +transaction.onabort = function() { + console.log("Error", transaction.error); +}; +``` + +### Event delegation + +Do we need onerror/onsuccess for every request? Not every time. We can use event delegation instead. + +**IndexedDB events bubble: `request` -> `transaction` -> `database`.** + +All events are DOM events, with capturing and bubbling, but usually only bubbling stage is used. + +So we can catch all errors using `db.onerror` handler, for reporting or other purposes: + +```js +db.onerror = function(event) { + let request = event.target; // the request that caused the error + + console.log("Error", request.error); +}; +``` + +...But what if an error is fully handled? We don't want to report it in that case. + +We can stop the bubbling and hence `db.onerror` by using `event.stopPropagation()` in `request.onerror`. + +```js +request.onerror = function(event) { + if (request.error.name == "ConstraintError") { + console.log("Book with such id already exists"); // handle the error + event.preventDefault(); // don't abort the transaction + event.stopPropagation(); // don't bubble error up, "chew" it + } else { + // do nothing + // transaction will be aborted + // we can take care of error in transaction.onabort + } +}; +``` + +## Searching by keys + +There are two main ways to search in an object store: +1. By a key or a key range. That is: by `book.id` in our "books" storage. +2. By another object field, e.g. `book.price`. We need an index for that. + +First let's deal with the keys and key ranges `(1)`. + +Methods that involve searching support either exact keys or so-called "range queries" -- [IDBKeyRange](https://www.w3.org/TR/IndexedDB/#keyrange) objects that specify a "key range". + +Ranges are created using following calls: + +- `IDBKeyRange.lowerBound(lower, [open])` means: `>lower` (or `≥lower` if `open` is true) +- `IDBKeyRange.upperBound(upper, [open])` means: `= 'js' +books.getAllKeys(IDBKeyRange.lowerBound('js', true)) +``` + +```smart header="Object store is always sorted" +Object store sorts values by key internally. + +So requests that return many values always return them in sorted by key order. +``` + + +## Searching by any field with an index + +To search by other object fields, we need to create an additional data structure named "index". + +An index is an "add-on" to the store that tracks a given object field. For each value of that field, it stores a list of keys for objects that have that value. There will be a more detailed picture below. + +The syntax: + +```js +objectStore.createIndex(name, keyPath, [options]); +``` + +- **`name`** -- index name, +- **`keyPath`** -- path to the object field that the index should track (we're going to search by that field), +- **`option`** -- an optional object with properties: + - **`unique`** -- if true, then there may be only one object in the store with the given value at the `keyPath`. The index will enforce that by generating an error if we try to add a duplicate. + - **`multiEntry`** -- only used if there value on `keyPath` is an array. In that case, by default, the index will treat the whole array as the key. But if `multiEntry` is true, then the index will keep a list of store objects for each value in that array. So array members become index keys. + +In our example, we store books keyed by `id`. + +Let's say we want to search by `price`. + +First, we need to create an index. It must be done in `upgradeneeded`, just like an object store: + +```js +openRequest.onupgradeneeded = function() { + // we must create the index here, in versionchange transaction + let books = db.createObjectStore('books', {keyPath: 'id'}); +*!* + let index = inventory.createIndex('price_idx', 'price'); +*/!* +}; +``` + +- The index will track `price` field. +- The price is not unique, there may be multiple books with the same price, so we don't set `unique` option. +- The price is not an array, so `multiEntry` flag is not applicable. + +Imagine that our `inventory` has 4 books. Here's the picture that shows exactly what the `index` is: + +![](indexeddb-index.png) + +As said, the index for each value of `price` (second argument) keeps the list of keys that have that price. + +The index keeps itself up to date automatically, we don't have to care about it. + +Now, when we want to search for a given price, we simply apply the same search methods to the index: + +```js +let transaction = db.transaction("books"); // readonly +let books = transaction.objectStore("books"); +let priceIndex = books.index("price_idx"); + +*!* +let request = priceIndex.getAll(10); +*/!* + +request.onsuccess = function() { + if (request.result !== undefined) { + console.log("Books", request.result); // array of books with price=10 + } else { + console.log("No such books"); + } +}; +``` + +We can also use `IDBKeyRange` to create ranges and looks for cheap/expensive books: + +```js +// find books where price < 5 +let request = priceIndex.getAll(IDBKeyRange.upperBound(5)); +``` + +Indexes are internally sorted by the tracked object field, `price` in our case. So when we do the search, the results are also sorted by `price`. + +## Deleting from store + +The `delete` method looks up values to delete by a query, just like `getAll`. + +- **`delete(query)`** -- delete matching values by query. + +For instance: +```js +// delete the book with id='js' +books.delete('js'); +``` + +If we'd like to delete books based on a price or another object field, then we should first find the key in the index, and then call `delete`: + +```js +// find the key where price = 5 +let request = priceIndex.getKey(5); + +request.onsuccess = function() { + let id = request.result; + let deleteRequest = books.delete(id); +}; +``` + +To delete everything: +```js +books.clear(); // clear the storage. +``` + +## Cursors + +Methods like `getAll/getAllKeys` return an array of keys/values. + +But an object storage can be huge, bigger than the available memory. + +Then `getAll` will fail to get all records as an array. + +What to do? + +Cursors provide the means to work around that. + +**A *cursor* is a special object that traverses the object storage, given a query, and returns one key/value at a time, thus saving memory.** + +As an object store is sorted internally by key, a cursor walks the store in key order (ascending by default). + +The syntax: +```js +// like getAll, but with a cursor: +let request = store.openCursor(query, [direction]); + +// to get keys, not values (like getAllKeys): store.openKeyCursor +``` + +- **`query`** is a key or a key range, same as for `getAll`. +- **`direction`** is an optional argument, which order to use: + - `"next"` -- the default, the cursor walks up from the record with the lowest key. + - `"prev"` -- the reverse order: down from the record with the biggest key. + - `"nextunique"`, `"prevunique"` -- same as above, but skip records with the same key (only for cursors over indexes, e.g. for multiple books with price=5 only the first one will be returned). + +**The main difference of the cursor is that `request.onsuccess` triggers multiple times: once for each result.** + +Here's an example of how to use a cursor: + +```js +let transaction = db.transaction("books"); +let books = transaction.objectStore("books"); + +let request = books.openCursor(); + +// called for each book found by the cursor +request.onsuccess = function() { + let cursor = request.result; + if (cursor) { + let key = cursor.key; // book key (id field) + let value = cursor.value; // book object + console.log(key, value); + cursor.continue(); + } else { + console.log("No more books"); + } +}; +``` + +The main cursor methods are: + +- `advance(count)` -- advance the cursor `count` times, skipping values. +- `continue([key])` -- advance the cursor to the next value in range matching or after key. + +Whether there are more values matching the cursor or not -- `onsuccess` gets called, and then in `result` we can get the cursor pointing to the next record, or `undefined`. + +In the example above the cursor was made for the object store. + +But we also can make a cursor over an index. As we remember, indexes allow to search by an object field. Cursors over indexes to precisely the same as over object stores -- they save memory by returning one value at a timee. + +For cursors over indexes, `cursor.key` is the index key (e.g. price), and we should use `cursor.primaryKey` property the object key: + +```js +let request = priceIdx.openCursor(IDBKeyRange.upperBound(5)); + +// called for each record +request.onsuccess = function() { + let cursor = request.result; + if (cursor) { + let key = cursor.primaryKey; // next object store key (id field) + let value = cursor.value; // next object store object (book object) + let key = cursor.key; // next index key (price) + console.log(key, value); + cursor.continue(); + } else { + console.log("No more books"); + } +}; +``` + +## Promise wrapper + +Adding `onsuccess/onerror` to every request is quite a cumbersome task. Sometimes we can make our life easier by using event delegation, e.g. set handlers on the whole transactions, but `async/await` is much more convenient. + +Let's use a thin promise wrapper further in this chapter. It creates a global `idb` object with [promisified](info:promisify) IndexedDB methods. + +Then, instead of `onsuccess/onerror` we can write like this: + +```js +let db = await idb.openDb('store', 1, db => { + if (db.oldVersion == 0) { + // perform the initialization + db.createObjectStore('books', {keyPath: 'id'}); + } +}); + +let transaction = db.transaction('books', 'readwrite'); +let books = transaction.objectStore('books'); + +try { + await books.add(...); + await books.add(...); + + await transaction.complete; + + console.log('jsbook saved'); +} catch(err) { + console.log('error', err.message); +} + +``` + +So we have all the sweet "plain async code" and "try..catch" stuff. + +### Error handling + +If we don't catch the error, then it falls through, just as usual. + +An uncaught error becomes an "unhandled promise rejection" event on `window` object. + +We can handle such errors like this: + +```js +window.addEventListener('unhandledrejection', event => { + let request = event.target; // IndexedDB native request object + let error = event.reason; // Unhandled error object, same as request.error + ...report about the error... +}); +``` + +### "Inactive transaction" pitfall + +A we know already, a transaction auto-commits as soon as the browser is done with the current code and microtasks. So if we put an *macrotask* like `fetch` in the middle of a transaction, then the transaction won't wait for it to finish. It just auto-commits. So the next request in it fails. + +For a promise wrapper and `async/await` the situation is the same. + +Here's an example of `fetch` in the middle of the transaction: + +```js +let transaction = db.transaction("inventory", "readwrite"); +let inventory = transaction.objectStore("inventory"); + +await inventory.add({ id: 'js', price: 10, created: new Date() }); + +await fetch(...); // (*) + +await inventory.add({ id: 'js', price: 10, created: new Date() }); // Error +``` + +The next `inventory.add` after `fetch` `(*)` fails with an "inactive transaction" error, because the transaction is already committed and closed at that time. + +The workaround is same as when working with native IndexedDB: either make a new transaction or just split things apart. +1. Prepare the data and fetch all that's needed first. +2. Then save in the database. + +### Getting native objects + +Internally, the wrapper performs a native IndexedDB request, adding `onerror/onsuccess` to it, and returns a promise that rejects/resolves with the result. + +That works most fine of the time. The examples are at the lib page . + +In few rare cases, when we need the original `request` object, we can access it as `promise.request` property of the promise: + +```js +let promise = books.add(book); // get a promise (don't await for its result) + +let request = promise.request; // native request object +let transaction = request.transaction; // native transaction object + +// ...do some native IndexedDB voodoo... + +let result = await promise; // if still needed +``` + +## Summary + +IndexedDB can be thought of as a "localStorage on steroids". It's a simple key-value database, powerful enough for offline apps, yet simple to use. + +The best manual is the specification, [the current one](https://w3c.github.io/IndexedDB) is 2.0, but few methods from [3.0](https://w3c.github.io/IndexedDB/) (it's not much different) are partially supported. + +The usage can be described with a few phrases: + +1. Get a promise wrapper like [idb](https://github.com/jakearchibald/idb). +2. Open a database: `idb.openDb(name, version, onupgradeneeded)` + - Create object storages in indexes in `onupgradeneeded` handlers. + - Update version if needed - either by comparing numbers or just checking what exists. +3. For requests: + - Create transaction `db.transaction('books')` (readwrite if needed). + - Get the object store `transaction.objectStore('books')`. +4. Then, to search by a key, call methods on the object store directly. + - To search by an object field, create an index. +5. If the data does not fit in memory, use a cursor. + +Here's a small demo app: + +[codetabs src="books" current="index.html"] diff --git a/6-data-storage/03-indexeddb/books.view/index.html b/6-data-storage/03-indexeddb/books.view/index.html new file mode 100644 index 000000000..11c12da7b --- /dev/null +++ b/6-data-storage/03-indexeddb/books.view/index.html @@ -0,0 +1,70 @@ + + + + + + +

          Books list:

          + +
            + + diff --git a/6-data-storage/03-indexeddb/indexeddb-cursor.png b/6-data-storage/03-indexeddb/indexeddb-cursor.png new file mode 100644 index 000000000..7c53219c9 Binary files /dev/null and b/6-data-storage/03-indexeddb/indexeddb-cursor.png differ diff --git a/6-data-storage/03-indexeddb/indexeddb-cursor@2x.png b/6-data-storage/03-indexeddb/indexeddb-cursor@2x.png new file mode 100644 index 000000000..f4392edfe Binary files /dev/null and b/6-data-storage/03-indexeddb/indexeddb-cursor@2x.png differ diff --git a/6-data-storage/03-indexeddb/indexeddb-index.png b/6-data-storage/03-indexeddb/indexeddb-index.png new file mode 100644 index 000000000..4fdc18976 Binary files /dev/null and b/6-data-storage/03-indexeddb/indexeddb-index.png differ diff --git a/6-data-storage/03-indexeddb/indexeddb-index@2x.png b/6-data-storage/03-indexeddb/indexeddb-index@2x.png new file mode 100644 index 000000000..406890304 Binary files /dev/null and b/6-data-storage/03-indexeddb/indexeddb-index@2x.png differ diff --git a/6-data-storage/03-indexeddb/indexeddb-structure.png b/6-data-storage/03-indexeddb/indexeddb-structure.png new file mode 100644 index 000000000..642e4d742 Binary files /dev/null and b/6-data-storage/03-indexeddb/indexeddb-structure.png differ diff --git a/6-data-storage/03-indexeddb/indexeddb-structure@2x.png b/6-data-storage/03-indexeddb/indexeddb-structure@2x.png new file mode 100644 index 000000000..2e4f389fd Binary files /dev/null and b/6-data-storage/03-indexeddb/indexeddb-structure@2x.png differ diff --git a/6-data-storage/index.md b/6-data-storage/index.md new file mode 100644 index 000000000..a08c05b58 --- /dev/null +++ b/6-data-storage/index.md @@ -0,0 +1,2 @@ + +# Storing data in the browser diff --git a/3-animation/1-bezier-curve/article.md b/7-animation/1-bezier-curve/article.md similarity index 100% rename from 3-animation/1-bezier-curve/article.md rename to 7-animation/1-bezier-curve/article.md diff --git a/7-animation/1-bezier-curve/bezier-car.png b/7-animation/1-bezier-curve/bezier-car.png new file mode 100644 index 000000000..32d959655 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier-car.png differ diff --git a/7-animation/1-bezier-curve/bezier-car@2x.png b/7-animation/1-bezier-curve/bezier-car@2x.png new file mode 100644 index 000000000..9debd3ba9 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier-car@2x.png differ diff --git a/7-animation/1-bezier-curve/bezier-letter.png b/7-animation/1-bezier-curve/bezier-letter.png new file mode 100644 index 000000000..fa9d23225 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier-letter.png differ diff --git a/7-animation/1-bezier-curve/bezier-letter@2x.png b/7-animation/1-bezier-curve/bezier-letter@2x.png new file mode 100644 index 000000000..e7e59ffd2 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier-letter@2x.png differ diff --git a/7-animation/1-bezier-curve/bezier-vase.png b/7-animation/1-bezier-curve/bezier-vase.png new file mode 100644 index 000000000..6c92f2466 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier-vase.png differ diff --git a/7-animation/1-bezier-curve/bezier-vase@2x.png b/7-animation/1-bezier-curve/bezier-vase@2x.png new file mode 100644 index 000000000..ccb977d7c Binary files /dev/null and b/7-animation/1-bezier-curve/bezier-vase@2x.png differ diff --git a/7-animation/1-bezier-curve/bezier2.png b/7-animation/1-bezier-curve/bezier2.png new file mode 100644 index 000000000..50df50963 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier2.png differ diff --git a/7-animation/1-bezier-curve/bezier2@2x.png b/7-animation/1-bezier-curve/bezier2@2x.png new file mode 100644 index 000000000..66cb9a211 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier2@2x.png differ diff --git a/7-animation/1-bezier-curve/bezier3-draw1.png b/7-animation/1-bezier-curve/bezier3-draw1.png new file mode 100644 index 000000000..2f3b3f8e3 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier3-draw1.png differ diff --git a/7-animation/1-bezier-curve/bezier3-draw1@2x.png b/7-animation/1-bezier-curve/bezier3-draw1@2x.png new file mode 100644 index 000000000..bd9ff640c Binary files /dev/null and b/7-animation/1-bezier-curve/bezier3-draw1@2x.png differ diff --git a/7-animation/1-bezier-curve/bezier3-draw2.png b/7-animation/1-bezier-curve/bezier3-draw2.png new file mode 100644 index 000000000..aead6cea5 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier3-draw2.png differ diff --git a/7-animation/1-bezier-curve/bezier3-draw2@2x.png b/7-animation/1-bezier-curve/bezier3-draw2@2x.png new file mode 100644 index 000000000..8d03793ea Binary files /dev/null and b/7-animation/1-bezier-curve/bezier3-draw2@2x.png differ diff --git a/7-animation/1-bezier-curve/bezier3-e.png b/7-animation/1-bezier-curve/bezier3-e.png new file mode 100644 index 000000000..0e9b8d372 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier3-e.png differ diff --git a/7-animation/1-bezier-curve/bezier3-e@2x.png b/7-animation/1-bezier-curve/bezier3-e@2x.png new file mode 100644 index 000000000..21ad2bb12 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier3-e@2x.png differ diff --git a/7-animation/1-bezier-curve/bezier3.png b/7-animation/1-bezier-curve/bezier3.png new file mode 100644 index 000000000..0af6c479a Binary files /dev/null and b/7-animation/1-bezier-curve/bezier3.png differ diff --git a/7-animation/1-bezier-curve/bezier3@2x.png b/7-animation/1-bezier-curve/bezier3@2x.png new file mode 100644 index 000000000..1390cbaa7 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier3@2x.png differ diff --git a/7-animation/1-bezier-curve/bezier4-e.png b/7-animation/1-bezier-curve/bezier4-e.png new file mode 100644 index 000000000..4f0bcae59 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier4-e.png differ diff --git a/7-animation/1-bezier-curve/bezier4-e@2x.png b/7-animation/1-bezier-curve/bezier4-e@2x.png new file mode 100644 index 000000000..34e89e632 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier4-e@2x.png differ diff --git a/7-animation/1-bezier-curve/bezier4.png b/7-animation/1-bezier-curve/bezier4.png new file mode 100644 index 000000000..f4ebc4a3c Binary files /dev/null and b/7-animation/1-bezier-curve/bezier4.png differ diff --git a/7-animation/1-bezier-curve/bezier4@2x.png b/7-animation/1-bezier-curve/bezier4@2x.png new file mode 100644 index 000000000..ddf3c2ec4 Binary files /dev/null and b/7-animation/1-bezier-curve/bezier4@2x.png differ diff --git a/3-animation/1-bezier-curve/demo.svg b/7-animation/1-bezier-curve/demo.svg similarity index 100% rename from 3-animation/1-bezier-curve/demo.svg rename to 7-animation/1-bezier-curve/demo.svg diff --git a/3-animation/1-bezier-curve/pause.png b/7-animation/1-bezier-curve/pause.png similarity index 100% rename from 3-animation/1-bezier-curve/pause.png rename to 7-animation/1-bezier-curve/pause.png diff --git a/3-animation/1-bezier-curve/play.png b/7-animation/1-bezier-curve/play.png similarity index 100% rename from 3-animation/1-bezier-curve/play.png rename to 7-animation/1-bezier-curve/play.png diff --git a/3-animation/2-css-animations/1-animate-logo-css/solution.md b/7-animation/2-css-animations/1-animate-logo-css/solution.md similarity index 100% rename from 3-animation/2-css-animations/1-animate-logo-css/solution.md rename to 7-animation/2-css-animations/1-animate-logo-css/solution.md diff --git a/3-animation/2-css-animations/1-animate-logo-css/solution.view/index.html b/7-animation/2-css-animations/1-animate-logo-css/solution.view/index.html similarity index 100% rename from 3-animation/2-css-animations/1-animate-logo-css/solution.view/index.html rename to 7-animation/2-css-animations/1-animate-logo-css/solution.view/index.html diff --git a/3-animation/2-css-animations/1-animate-logo-css/source.view/index.html b/7-animation/2-css-animations/1-animate-logo-css/source.view/index.html similarity index 100% rename from 3-animation/2-css-animations/1-animate-logo-css/source.view/index.html rename to 7-animation/2-css-animations/1-animate-logo-css/source.view/index.html diff --git a/3-animation/2-css-animations/1-animate-logo-css/task.md b/7-animation/2-css-animations/1-animate-logo-css/task.md similarity index 100% rename from 3-animation/2-css-animations/1-animate-logo-css/task.md rename to 7-animation/2-css-animations/1-animate-logo-css/task.md diff --git a/7-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up.png b/7-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up.png new file mode 100644 index 000000000..2318e2c03 Binary files /dev/null and b/7-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up.png differ diff --git a/7-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up@2x.png b/7-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up@2x.png new file mode 100644 index 000000000..91155c099 Binary files /dev/null and b/7-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up@2x.png differ diff --git a/3-animation/2-css-animations/2-animate-logo-bezier-css/solution.md b/7-animation/2-css-animations/2-animate-logo-bezier-css/solution.md similarity index 100% rename from 3-animation/2-css-animations/2-animate-logo-bezier-css/solution.md rename to 7-animation/2-css-animations/2-animate-logo-bezier-css/solution.md diff --git a/3-animation/2-css-animations/2-animate-logo-bezier-css/solution.view/index.html b/7-animation/2-css-animations/2-animate-logo-bezier-css/solution.view/index.html similarity index 100% rename from 3-animation/2-css-animations/2-animate-logo-bezier-css/solution.view/index.html rename to 7-animation/2-css-animations/2-animate-logo-bezier-css/solution.view/index.html diff --git a/3-animation/2-css-animations/2-animate-logo-bezier-css/task.md b/7-animation/2-css-animations/2-animate-logo-bezier-css/task.md similarity index 100% rename from 3-animation/2-css-animations/2-animate-logo-bezier-css/task.md rename to 7-animation/2-css-animations/2-animate-logo-bezier-css/task.md diff --git a/6-async/04-promise-api/02-promise-errors-as-results-2/solution.md b/7-animation/2-css-animations/3-animate-circle/solution.md similarity index 100% rename from 6-async/04-promise-api/02-promise-errors-as-results-2/solution.md rename to 7-animation/2-css-animations/3-animate-circle/solution.md diff --git a/3-animation/2-css-animations/3-animate-circle/solution.view/index.html b/7-animation/2-css-animations/3-animate-circle/solution.view/index.html similarity index 100% rename from 3-animation/2-css-animations/3-animate-circle/solution.view/index.html rename to 7-animation/2-css-animations/3-animate-circle/solution.view/index.html diff --git a/3-animation/2-css-animations/3-animate-circle/source.view/index.html b/7-animation/2-css-animations/3-animate-circle/source.view/index.html similarity index 100% rename from 3-animation/2-css-animations/3-animate-circle/source.view/index.html rename to 7-animation/2-css-animations/3-animate-circle/source.view/index.html diff --git a/3-animation/2-css-animations/3-animate-circle/task.md b/7-animation/2-css-animations/3-animate-circle/task.md similarity index 100% rename from 3-animation/2-css-animations/3-animate-circle/task.md rename to 7-animation/2-css-animations/3-animate-circle/task.md diff --git a/3-animation/2-css-animations/article.md b/7-animation/2-css-animations/article.md similarity index 100% rename from 3-animation/2-css-animations/article.md rename to 7-animation/2-css-animations/article.md diff --git a/7-animation/2-css-animations/bezier-linear.png b/7-animation/2-css-animations/bezier-linear.png new file mode 100644 index 000000000..0b1e53bb2 Binary files /dev/null and b/7-animation/2-css-animations/bezier-linear.png differ diff --git a/7-animation/2-css-animations/bezier-linear@2x.png b/7-animation/2-css-animations/bezier-linear@2x.png new file mode 100644 index 000000000..fa7963918 Binary files /dev/null and b/7-animation/2-css-animations/bezier-linear@2x.png differ diff --git a/7-animation/2-css-animations/bezier-train-over.png b/7-animation/2-css-animations/bezier-train-over.png new file mode 100644 index 000000000..ebe51a8a9 Binary files /dev/null and b/7-animation/2-css-animations/bezier-train-over.png differ diff --git a/7-animation/2-css-animations/bezier-train-over@2x.png b/7-animation/2-css-animations/bezier-train-over@2x.png new file mode 100644 index 000000000..2b444d66a Binary files /dev/null and b/7-animation/2-css-animations/bezier-train-over@2x.png differ diff --git a/3-animation/2-css-animations/boat.view/index.html b/7-animation/2-css-animations/boat.view/index.html similarity index 100% rename from 3-animation/2-css-animations/boat.view/index.html rename to 7-animation/2-css-animations/boat.view/index.html diff --git a/3-animation/2-css-animations/boat.view/style.css b/7-animation/2-css-animations/boat.view/style.css similarity index 100% rename from 3-animation/2-css-animations/boat.view/style.css rename to 7-animation/2-css-animations/boat.view/style.css diff --git a/3-animation/2-css-animations/digits-negative-delay.view/index.html b/7-animation/2-css-animations/digits-negative-delay.view/index.html similarity index 100% rename from 3-animation/2-css-animations/digits-negative-delay.view/index.html rename to 7-animation/2-css-animations/digits-negative-delay.view/index.html diff --git a/3-animation/2-css-animations/digits-negative-delay.view/script.js b/7-animation/2-css-animations/digits-negative-delay.view/script.js similarity index 100% rename from 3-animation/2-css-animations/digits-negative-delay.view/script.js rename to 7-animation/2-css-animations/digits-negative-delay.view/script.js diff --git a/3-animation/2-css-animations/digits-negative-delay.view/style.css b/7-animation/2-css-animations/digits-negative-delay.view/style.css similarity index 100% rename from 3-animation/2-css-animations/digits-negative-delay.view/style.css rename to 7-animation/2-css-animations/digits-negative-delay.view/style.css diff --git a/3-animation/2-css-animations/digits.view/index.html b/7-animation/2-css-animations/digits.view/index.html similarity index 100% rename from 3-animation/2-css-animations/digits.view/index.html rename to 7-animation/2-css-animations/digits.view/index.html diff --git a/3-animation/2-css-animations/digits.view/script.js b/7-animation/2-css-animations/digits.view/script.js similarity index 100% rename from 3-animation/2-css-animations/digits.view/script.js rename to 7-animation/2-css-animations/digits.view/script.js diff --git a/3-animation/2-css-animations/digits.view/style.css b/7-animation/2-css-animations/digits.view/style.css similarity index 100% rename from 3-animation/2-css-animations/digits.view/style.css rename to 7-animation/2-css-animations/digits.view/style.css diff --git a/7-animation/2-css-animations/ease-in-out.png b/7-animation/2-css-animations/ease-in-out.png new file mode 100644 index 000000000..e0b179b6b Binary files /dev/null and b/7-animation/2-css-animations/ease-in-out.png differ diff --git a/7-animation/2-css-animations/ease-in-out@2x.png b/7-animation/2-css-animations/ease-in-out@2x.png new file mode 100644 index 000000000..14726b33c Binary files /dev/null and b/7-animation/2-css-animations/ease-in-out@2x.png differ diff --git a/7-animation/2-css-animations/ease-in.png b/7-animation/2-css-animations/ease-in.png new file mode 100644 index 000000000..df182377a Binary files /dev/null and b/7-animation/2-css-animations/ease-in.png differ diff --git a/7-animation/2-css-animations/ease-in@2x.png b/7-animation/2-css-animations/ease-in@2x.png new file mode 100644 index 000000000..2d6e7ddbf Binary files /dev/null and b/7-animation/2-css-animations/ease-in@2x.png differ diff --git a/7-animation/2-css-animations/ease-out.png b/7-animation/2-css-animations/ease-out.png new file mode 100644 index 000000000..c07ed1234 Binary files /dev/null and b/7-animation/2-css-animations/ease-out.png differ diff --git a/7-animation/2-css-animations/ease-out@2x.png b/7-animation/2-css-animations/ease-out@2x.png new file mode 100644 index 000000000..b49506a14 Binary files /dev/null and b/7-animation/2-css-animations/ease-out@2x.png differ diff --git a/7-animation/2-css-animations/ease.png b/7-animation/2-css-animations/ease.png new file mode 100644 index 000000000..37f5376b7 Binary files /dev/null and b/7-animation/2-css-animations/ease.png differ diff --git a/7-animation/2-css-animations/ease@2x.png b/7-animation/2-css-animations/ease@2x.png new file mode 100644 index 000000000..a77eb1036 Binary files /dev/null and b/7-animation/2-css-animations/ease@2x.png differ diff --git a/3-animation/2-css-animations/step-end.view/index.html b/7-animation/2-css-animations/step-end.view/index.html similarity index 100% rename from 3-animation/2-css-animations/step-end.view/index.html rename to 7-animation/2-css-animations/step-end.view/index.html diff --git a/3-animation/2-css-animations/step-end.view/style.css b/7-animation/2-css-animations/step-end.view/style.css similarity index 100% rename from 3-animation/2-css-animations/step-end.view/style.css rename to 7-animation/2-css-animations/step-end.view/style.css diff --git a/3-animation/2-css-animations/step-list.view/index.html b/7-animation/2-css-animations/step-list.view/index.html similarity index 100% rename from 3-animation/2-css-animations/step-list.view/index.html rename to 7-animation/2-css-animations/step-list.view/index.html diff --git a/3-animation/2-css-animations/step-list.view/style.css b/7-animation/2-css-animations/step-list.view/style.css similarity index 100% rename from 3-animation/2-css-animations/step-list.view/style.css rename to 7-animation/2-css-animations/step-list.view/style.css diff --git a/3-animation/2-css-animations/step.view/index.html b/7-animation/2-css-animations/step.view/index.html similarity index 100% rename from 3-animation/2-css-animations/step.view/index.html rename to 7-animation/2-css-animations/step.view/index.html diff --git a/3-animation/2-css-animations/step.view/style.css b/7-animation/2-css-animations/step.view/style.css similarity index 100% rename from 3-animation/2-css-animations/step.view/style.css rename to 7-animation/2-css-animations/step.view/style.css diff --git a/7-animation/2-css-animations/train-curve.png b/7-animation/2-css-animations/train-curve.png new file mode 100644 index 000000000..97b722944 Binary files /dev/null and b/7-animation/2-css-animations/train-curve.png differ diff --git a/7-animation/2-css-animations/train-curve@2x.png b/7-animation/2-css-animations/train-curve@2x.png new file mode 100644 index 000000000..40e8947fc Binary files /dev/null and b/7-animation/2-css-animations/train-curve@2x.png differ diff --git a/3-animation/2-css-animations/train-linear.view/index.html b/7-animation/2-css-animations/train-linear.view/index.html similarity index 100% rename from 3-animation/2-css-animations/train-linear.view/index.html rename to 7-animation/2-css-animations/train-linear.view/index.html diff --git a/3-animation/2-css-animations/train-linear.view/style.css b/7-animation/2-css-animations/train-linear.view/style.css similarity index 100% rename from 3-animation/2-css-animations/train-linear.view/style.css rename to 7-animation/2-css-animations/train-linear.view/style.css diff --git a/3-animation/2-css-animations/train-over.view/index.html b/7-animation/2-css-animations/train-over.view/index.html similarity index 100% rename from 3-animation/2-css-animations/train-over.view/index.html rename to 7-animation/2-css-animations/train-over.view/index.html diff --git a/3-animation/2-css-animations/train-over.view/style.css b/7-animation/2-css-animations/train-over.view/style.css similarity index 100% rename from 3-animation/2-css-animations/train-over.view/style.css rename to 7-animation/2-css-animations/train-over.view/style.css diff --git a/3-animation/2-css-animations/train.view/index.html b/7-animation/2-css-animations/train.view/index.html similarity index 100% rename from 3-animation/2-css-animations/train.view/index.html rename to 7-animation/2-css-animations/train.view/index.html diff --git a/3-animation/2-css-animations/train.view/style.css b/7-animation/2-css-animations/train.view/style.css similarity index 100% rename from 3-animation/2-css-animations/train.view/style.css rename to 7-animation/2-css-animations/train.view/style.css diff --git a/3-animation/3-js-animation/1-animate-ball/solution.md b/7-animation/3-js-animation/1-animate-ball/solution.md similarity index 100% rename from 3-animation/3-js-animation/1-animate-ball/solution.md rename to 7-animation/3-js-animation/1-animate-ball/solution.md diff --git a/3-animation/3-js-animation/1-animate-ball/solution.view/index.html b/7-animation/3-js-animation/1-animate-ball/solution.view/index.html similarity index 100% rename from 3-animation/3-js-animation/1-animate-ball/solution.view/index.html rename to 7-animation/3-js-animation/1-animate-ball/solution.view/index.html diff --git a/3-animation/3-js-animation/1-animate-ball/solution.view/style.css b/7-animation/3-js-animation/1-animate-ball/solution.view/style.css similarity index 100% rename from 3-animation/3-js-animation/1-animate-ball/solution.view/style.css rename to 7-animation/3-js-animation/1-animate-ball/solution.view/style.css diff --git a/3-animation/3-js-animation/1-animate-ball/source.view/index.html b/7-animation/3-js-animation/1-animate-ball/source.view/index.html similarity index 100% rename from 3-animation/3-js-animation/1-animate-ball/source.view/index.html rename to 7-animation/3-js-animation/1-animate-ball/source.view/index.html diff --git a/3-animation/3-js-animation/1-animate-ball/source.view/style.css b/7-animation/3-js-animation/1-animate-ball/source.view/style.css similarity index 100% rename from 3-animation/3-js-animation/1-animate-ball/source.view/style.css rename to 7-animation/3-js-animation/1-animate-ball/source.view/style.css diff --git a/3-animation/3-js-animation/1-animate-ball/task.md b/7-animation/3-js-animation/1-animate-ball/task.md similarity index 100% rename from 3-animation/3-js-animation/1-animate-ball/task.md rename to 7-animation/3-js-animation/1-animate-ball/task.md diff --git a/3-animation/3-js-animation/2-animate-ball-hops/solution.md b/7-animation/3-js-animation/2-animate-ball-hops/solution.md similarity index 100% rename from 3-animation/3-js-animation/2-animate-ball-hops/solution.md rename to 7-animation/3-js-animation/2-animate-ball-hops/solution.md diff --git a/3-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html b/7-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html similarity index 100% rename from 3-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html rename to 7-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html diff --git a/3-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css b/7-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css similarity index 100% rename from 3-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css rename to 7-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css diff --git a/3-animation/3-js-animation/2-animate-ball-hops/task.md b/7-animation/3-js-animation/2-animate-ball-hops/task.md similarity index 100% rename from 3-animation/3-js-animation/2-animate-ball-hops/task.md rename to 7-animation/3-js-animation/2-animate-ball-hops/task.md diff --git a/3-animation/3-js-animation/article.md b/7-animation/3-js-animation/article.md similarity index 99% rename from 3-animation/3-js-animation/article.md rename to 7-animation/3-js-animation/article.md index e98ee8f03..0975ebff5 100644 --- a/3-animation/3-js-animation/article.md +++ b/7-animation/3-js-animation/article.md @@ -8,7 +8,7 @@ For instance, moving along a complex path, with a timing function different from An animation can be implemented as a sequence of frames -- usually small changes to HTML/CSS properties. -For instance, changing `style.left` from `0px` to `100px` moves the element. And if we increase it in `setInterval`, changing by `2px` with a tiny delay, like 50 times per second, then it looks smooth. That's the same principle as in the cinema: 24 or more frames per second is enough to make it look smooth. +For instance, changing `style.left` from `0px` to `100px` moves the element. And if we increase it in `setInterval`, changing by `2px` with a tiny delay, like 50 times per second, then it looks smooth. That's the same principle as in the cinema: 24 frames per second is enough to make it look smooth. The pseudo-code can look like this: diff --git a/7-animation/3-js-animation/back.png b/7-animation/3-js-animation/back.png new file mode 100644 index 000000000..7a5d9a6fb Binary files /dev/null and b/7-animation/3-js-animation/back.png differ diff --git a/3-animation/3-js-animation/back.view/index.html b/7-animation/3-js-animation/back.view/index.html similarity index 100% rename from 3-animation/3-js-animation/back.view/index.html rename to 7-animation/3-js-animation/back.view/index.html diff --git a/3-animation/3-js-animation/back.view/style.css b/7-animation/3-js-animation/back.view/style.css similarity index 100% rename from 3-animation/3-js-animation/back.view/style.css rename to 7-animation/3-js-animation/back.view/style.css diff --git a/7-animation/3-js-animation/back@2x.png b/7-animation/3-js-animation/back@2x.png new file mode 100644 index 000000000..dc90e1fcf Binary files /dev/null and b/7-animation/3-js-animation/back@2x.png differ diff --git a/7-animation/3-js-animation/bezier-linear.png b/7-animation/3-js-animation/bezier-linear.png new file mode 100644 index 000000000..0b1e53bb2 Binary files /dev/null and b/7-animation/3-js-animation/bezier-linear.png differ diff --git a/7-animation/3-js-animation/bezier-linear@2x.png b/7-animation/3-js-animation/bezier-linear@2x.png new file mode 100644 index 000000000..fa7963918 Binary files /dev/null and b/7-animation/3-js-animation/bezier-linear@2x.png differ diff --git a/3-animation/3-js-animation/bounce-easeinout.view/index.html b/7-animation/3-js-animation/bounce-easeinout.view/index.html similarity index 100% rename from 3-animation/3-js-animation/bounce-easeinout.view/index.html rename to 7-animation/3-js-animation/bounce-easeinout.view/index.html diff --git a/3-animation/3-js-animation/bounce-easeinout.view/style.css b/7-animation/3-js-animation/bounce-easeinout.view/style.css similarity index 100% rename from 3-animation/3-js-animation/bounce-easeinout.view/style.css rename to 7-animation/3-js-animation/bounce-easeinout.view/style.css diff --git a/3-animation/3-js-animation/bounce-easeout.view/index.html b/7-animation/3-js-animation/bounce-easeout.view/index.html similarity index 100% rename from 3-animation/3-js-animation/bounce-easeout.view/index.html rename to 7-animation/3-js-animation/bounce-easeout.view/index.html diff --git a/3-animation/3-js-animation/bounce-easeout.view/style.css b/7-animation/3-js-animation/bounce-easeout.view/style.css similarity index 100% rename from 3-animation/3-js-animation/bounce-easeout.view/style.css rename to 7-animation/3-js-animation/bounce-easeout.view/style.css diff --git a/7-animation/3-js-animation/bounce-inout.png b/7-animation/3-js-animation/bounce-inout.png new file mode 100644 index 000000000..5c867fd7d Binary files /dev/null and b/7-animation/3-js-animation/bounce-inout.png differ diff --git a/7-animation/3-js-animation/bounce-inout@2x.png b/7-animation/3-js-animation/bounce-inout@2x.png new file mode 100644 index 000000000..0527e4597 Binary files /dev/null and b/7-animation/3-js-animation/bounce-inout@2x.png differ diff --git a/3-animation/3-js-animation/bounce.view/index.html b/7-animation/3-js-animation/bounce.view/index.html similarity index 100% rename from 3-animation/3-js-animation/bounce.view/index.html rename to 7-animation/3-js-animation/bounce.view/index.html diff --git a/3-animation/3-js-animation/bounce.view/style.css b/7-animation/3-js-animation/bounce.view/style.css similarity index 100% rename from 3-animation/3-js-animation/bounce.view/style.css rename to 7-animation/3-js-animation/bounce.view/style.css diff --git a/7-animation/3-js-animation/circ-ease.png b/7-animation/3-js-animation/circ-ease.png new file mode 100644 index 000000000..2d3c6772e Binary files /dev/null and b/7-animation/3-js-animation/circ-ease.png differ diff --git a/7-animation/3-js-animation/circ-ease@2x.png b/7-animation/3-js-animation/circ-ease@2x.png new file mode 100644 index 000000000..5984fdb60 Binary files /dev/null and b/7-animation/3-js-animation/circ-ease@2x.png differ diff --git a/7-animation/3-js-animation/circ.png b/7-animation/3-js-animation/circ.png new file mode 100644 index 000000000..51663c431 Binary files /dev/null and b/7-animation/3-js-animation/circ.png differ diff --git a/3-animation/3-js-animation/circ.view/index.html b/7-animation/3-js-animation/circ.view/index.html similarity index 100% rename from 3-animation/3-js-animation/circ.view/index.html rename to 7-animation/3-js-animation/circ.view/index.html diff --git a/3-animation/3-js-animation/circ.view/style.css b/7-animation/3-js-animation/circ.view/style.css similarity index 100% rename from 3-animation/3-js-animation/circ.view/style.css rename to 7-animation/3-js-animation/circ.view/style.css diff --git a/7-animation/3-js-animation/circ@2x.png b/7-animation/3-js-animation/circ@2x.png new file mode 100644 index 000000000..55fdbf377 Binary files /dev/null and b/7-animation/3-js-animation/circ@2x.png differ diff --git a/7-animation/3-js-animation/elastic.png b/7-animation/3-js-animation/elastic.png new file mode 100644 index 000000000..db9c112a3 Binary files /dev/null and b/7-animation/3-js-animation/elastic.png differ diff --git a/3-animation/3-js-animation/elastic.view/index.html b/7-animation/3-js-animation/elastic.view/index.html similarity index 100% rename from 3-animation/3-js-animation/elastic.view/index.html rename to 7-animation/3-js-animation/elastic.view/index.html diff --git a/3-animation/3-js-animation/elastic.view/style.css b/7-animation/3-js-animation/elastic.view/style.css similarity index 100% rename from 3-animation/3-js-animation/elastic.view/style.css rename to 7-animation/3-js-animation/elastic.view/style.css diff --git a/7-animation/3-js-animation/elastic@2x.png b/7-animation/3-js-animation/elastic@2x.png new file mode 100644 index 000000000..46c50779a Binary files /dev/null and b/7-animation/3-js-animation/elastic@2x.png differ diff --git a/7-animation/3-js-animation/linear.png b/7-animation/3-js-animation/linear.png new file mode 100644 index 000000000..e19711c36 Binary files /dev/null and b/7-animation/3-js-animation/linear.png differ diff --git a/7-animation/3-js-animation/linear@2x.png b/7-animation/3-js-animation/linear@2x.png new file mode 100644 index 000000000..652dd175c Binary files /dev/null and b/7-animation/3-js-animation/linear@2x.png differ diff --git a/3-animation/3-js-animation/move-raf.view/index.html b/7-animation/3-js-animation/move-raf.view/index.html similarity index 100% rename from 3-animation/3-js-animation/move-raf.view/index.html rename to 7-animation/3-js-animation/move-raf.view/index.html diff --git a/3-animation/3-js-animation/move.view/index.html b/7-animation/3-js-animation/move.view/index.html similarity index 100% rename from 3-animation/3-js-animation/move.view/index.html rename to 7-animation/3-js-animation/move.view/index.html diff --git a/7-animation/3-js-animation/quad.png b/7-animation/3-js-animation/quad.png new file mode 100644 index 000000000..c757cb0e3 Binary files /dev/null and b/7-animation/3-js-animation/quad.png differ diff --git a/3-animation/3-js-animation/quad.view/index.html b/7-animation/3-js-animation/quad.view/index.html similarity index 100% rename from 3-animation/3-js-animation/quad.view/index.html rename to 7-animation/3-js-animation/quad.view/index.html diff --git a/3-animation/3-js-animation/quad.view/style.css b/7-animation/3-js-animation/quad.view/style.css similarity index 100% rename from 3-animation/3-js-animation/quad.view/style.css rename to 7-animation/3-js-animation/quad.view/style.css diff --git a/7-animation/3-js-animation/quad@2x.png b/7-animation/3-js-animation/quad@2x.png new file mode 100644 index 000000000..b170e3209 Binary files /dev/null and b/7-animation/3-js-animation/quad@2x.png differ diff --git a/7-animation/3-js-animation/quint.png b/7-animation/3-js-animation/quint.png new file mode 100644 index 000000000..a71b91ba2 Binary files /dev/null and b/7-animation/3-js-animation/quint.png differ diff --git a/3-animation/3-js-animation/quint.view/index.html b/7-animation/3-js-animation/quint.view/index.html similarity index 100% rename from 3-animation/3-js-animation/quint.view/index.html rename to 7-animation/3-js-animation/quint.view/index.html diff --git a/3-animation/3-js-animation/quint.view/style.css b/7-animation/3-js-animation/quint.view/style.css similarity index 100% rename from 3-animation/3-js-animation/quint.view/style.css rename to 7-animation/3-js-animation/quint.view/style.css diff --git a/7-animation/3-js-animation/quint@2x.png b/7-animation/3-js-animation/quint@2x.png new file mode 100644 index 000000000..0e6a305b1 Binary files /dev/null and b/7-animation/3-js-animation/quint@2x.png differ diff --git a/3-animation/3-js-animation/text.view/index.html b/7-animation/3-js-animation/text.view/index.html similarity index 100% rename from 3-animation/3-js-animation/text.view/index.html rename to 7-animation/3-js-animation/text.view/index.html diff --git a/3-animation/3-js-animation/text.view/style.css b/7-animation/3-js-animation/text.view/style.css similarity index 100% rename from 3-animation/3-js-animation/text.view/style.css rename to 7-animation/3-js-animation/text.view/style.css diff --git a/3-animation/3-js-animation/width.view/animate.js b/7-animation/3-js-animation/width.view/animate.js similarity index 100% rename from 3-animation/3-js-animation/width.view/animate.js rename to 7-animation/3-js-animation/width.view/animate.js diff --git a/3-animation/3-js-animation/width.view/index.html b/7-animation/3-js-animation/width.view/index.html similarity index 100% rename from 3-animation/3-js-animation/width.view/index.html rename to 7-animation/3-js-animation/width.view/index.html diff --git a/3-animation/index.md b/7-animation/index.md similarity index 100% rename from 3-animation/index.md rename to 7-animation/index.md diff --git a/7-network/1-xmlhttprequest/article.md b/7-network/1-xmlhttprequest/article.md deleted file mode 100644 index 52b113568..000000000 --- a/7-network/1-xmlhttprequest/article.md +++ /dev/null @@ -1,400 +0,0 @@ -# XMLHttpRequest and AJAX - -`XMLHttpRequest` is a built-in browser object that allows to make HTTP requests in JavaScript. - -Despite of having the word "XML" in its name, it can operate on any data, not only in XML format. - -## Asynchronous XMLHttpRequest - -XMLHttpRequest has two modes of operation: synchronous and asynchronous. - -First let's see the asynchronous variant as it's used in the majority of cases. - -The code below loads the URL at `/article/xmlhttprequest/hello.txt` from the server and shows its content on-screen: - -```js run -*!* -// 1. Create a new XMLHttpRequest object -*/!* -let xhr = new XMLHttpRequest(); - -*!* -// 2. Configure it: GET-request for the URL /article/.../hello.txt -xhr.open('GET', '/article/xmlhttprequest/hello.txt'); -*/!* - -*!* -// 3. Send the request over the network -*/!* -xhr.send(); - -*!* -// 4. This will be called after the response is received -*/!* -xhr.onload = function() { - if (xhr.status != 200) { // analyze HTTP status of the response - // if it's not 200, consider it an error - alert(xhr.status + ': ' + xhr.statusText); // e.g. 404: Not Found - } else { - // show the result - alert(xhr.responseText); // responseText is the server response - } -}; -``` - -As we can see, there are several methods of `XMLHttpRequest` here. Let's cover them. - -## Setup: "open" - -The syntax: -```js -xhr.open(method, URL, async, user, password) -``` - -This method is usually called first after `new XMLHttpRequest`. It specifies the main parameters of the request: - -- `method` -- HTTP-method. Usually `"GET"` or `"POST"`, but we can also use TRACE/DELETE/PUT and so on. -- `URL` -- the URL to request. Can use any path and protocol, but there are cross-domain limitations called "Same Origin Policy". We can make any requests to the same `protocol://domain:port` that the current page comes from, but other locations are "forbidden" by default (unless they implement special HTTP-headers, we'll cover them in chapter [todo]). -- `async` -- if the third parameter is explicitly set to `false`, then the request is synchronous, otherwise it's asynchronous. We'll talk more about that in this chapter soon. -- `user`, `password` -- login and password for basic HTTP auth (if required). - -Please note that `open` call, contrary to its name, does not open the connection. It only configures the request, but the network activity only starts with the call of `send`. - -## Send it out: "send" - -The syntax: -```js -xhr.send([body]) -``` - -This method opens the connection and sends the request to server. The optional `body` parameter contains the request body. Some request methods like `GET` do not have a body. And some of them like `POST` use `body` to send the data. We'll see examples with a body in the next chapter. - - -## Cancel: abort and timeout - -If we changed our mind, we can terminate the request at any time. The call to `xhr.abort()` does that: - -```js -xhr.abort(); // terminate the request -``` - -We can also specify a timeout using the corresponding property: - -```js -xhr.timeout = 10000; -``` - -The timeout is expressed in ms. If the request does not succeed within the given time, it gets canceled automatically. - -## Events: onload, onerror etc - -A request is asynchronous by default. In other words, the browser sends it out and allows other JavaScript code to execute. - -After the request is sent, `xhr` starts to generate events. We can use `addEventListener` or `on` properties to handle them, just like with DOM objects. - -The modern [specification](https://xhr.spec.whatwg.org/#events) lists following events: - -- `loadstart` -- the request has started. -- `progress` -- the browser received a data packet (can happen multiple times). -- `abort` -- the request was aborted by `xhr.abort()`. -- `error` -- an network error has occurred, the request failed. -- `load` -- the request is successful, no errors. -- `timeout` -- the request was canceled due to timeout (if the timeout is set). -- `loadend` -- the request is done (with an error or without it) -- `readystatechange` -- the request state is changed (will cover later). - -Using these events we can track successful loading (`onload`), errors (`onerror`) and the amount of the data loaded (`onprogress`). - -Please note that errors here are "communication errors". In other words, if the connection is lost or the remote server does not respond at all -- then it's the error in the terms of XMLHttpRequest. Bad HTTP status like 500 or 404 are not considered errors. - -Here's a more feature-full example, with errors and a timeout: - -```html run - - - - - - -``` - -1. The first button triggers only `onload` as it loads the file `hello.txt` normally. -2. The second button loads a very slow URL, so it calls only `ontimeout` (because `xhr.timeout` is set). -3. The third button loads a non-existant URL, but it also calls `onload` (with "Loaded: 404"), because there's no network error. -4. The last button tries to load a page from another domain. That's prohibited unless the remote server explicitly agrees by sending certain headers (to be covered later), so we have `onerror` here. The `onerror` handler would also trigger in other cases if we start a request, and then sever the network connection of our device. - -## Response: status, responseText and others - -Once the server has responded, we can receive the result in the following properties of the request object: - -`status` -: HTTP status code: `200`, `404`, `403` and so on. Also can be `0` if an error occurred. - -`statusText` -: HTTP status message: usually `OK` for `200`, `Not Found` for `404`, `Forbidden` for `403` and so on. - -`responseText` -: The text of the server response, - -If the server returns XML with the correct header `Content-type: text/xml`, then there's also `responseXML` property with the parsed XML document. You can query it with `xhr.responseXml.querySelector("...")` and perform other XML-specific operations. - -That's rarely used, because most of the time JSON is returned by the server. And then we can parse it using `JSON.parse(xhr.responseText)`. - -## Synchronous and asynchronous requests - -If in the `open` method the third parameter `async` is set to `false`, the request is made synchronously. - -In other words, Javascript execution pauses at that line and continues when the response is received. Somewhat like `alert` or `prompt` commands. - -Synchronous calls are used rarely, because they block in-page Javascript till the loading is complete. In some browsers, a user is unable to scroll the page. - -```js -// Synchronous request -xhr.open('GET', 'phones.json', *!*false*/!*); - -// Send it -xhr.send(); -*!* -// ...JavaScript "hangs" and waits till the request is complete -*/!* -``` - -If a synchronous call takes too much time, the browser may suggest to close the "hanging" webpage. - -Also, because of the blocking, it becomes impossible to send two requests simultaneously. And, looking a bit forward, let's note that some advanced capabilities of `XMLHttpRequest`, like requesting from another domain or specifying a timeout, are unavailable for synchronous requests. - -Because of all that, synchronous requests are used very sparingly, almost never. - -By default, requests are asynchronous. - -The same request made asynchronously: - -```js -let xhr = new XMLHttpRequest(); - -xhr.open('GET', 'phones.json'); // the third parameter is true by default - -xhr.send(); // (1) - -*!* -xhr.onreadystatechange = function() { // (3) - if (xhr.readyState != 4) return; -*/!* - - button.innerHTML = 'Complete!'; - - if (xhr.status != 200) { - alert(xhr.status + ': ' + xhr.statusText); - } else { - alert(xhr.responseText); - } - -} - -button.innerHTML = 'Loading...'; // (2) -button.disabled = true; -``` - -Now as there's no third argument in `open` (or if we explicitly set it to `true`), the request is asynchronous. In other words, after the call `xhr.send()` in the line `(1)`, Javascript does not "hang", but continues to execute. - -In our case, it means that `(2)` shows a "loading" message. - -Then, after time, when the result is received, it comes in the event handler `(3)` that we'll cover a bit later. - -```online -The full example in action: - -[codetabs src="phones-async"] -``` - -# Event "readystatechange" - -The event `readystatechange` occurs multiple times during sending the request and receiving the response. - -As the name suggests, there's a "ready state" of `XMLHttpRequest`. It is accessible as `xhr.readyState`. - -In the example above we only used state `4` (request complete), but there are few more. - -All states, as in [the specification](http://www.w3.org/TR/XMLHttpRequest/#states): - -```js -const unsigned short UNSENT = 0; // initial state -const unsigned short OPENED = 1; // open called -const unsigned short HEADERS_RECEIVED = 2; // response headers received -const unsigned short LOADING = 3; // response is loading (a data packed is received) -const unsigned short DONE = 4; // request complete -``` - -An `XMLHttpRequest` object travels them in the order `0` -> `1` -> `2` -> `3` -> ... -> `3` -> `4`. State `3` repeats every time a data packet is received over the network. - -The example above demonstrates these states. The server answers the request `digits` by sending a string of `1000` digits once per second. - -[codetabs src="readystate"] - -```warn header="Packets may break at any byte" -One might think that `readyState=3` (the next data packet is received) allows us to get the current (not full yet) response body in `responseText`. - -That's true. But only partially. - -Technically, we do not have control over breakpoints between network packets. Many languages use multi-byte encodings like UTF-8, where a character is represented by multiple bytes. Some characters use only 1 byte, some use 2 or more. And packets may split *in the middle of a character*. - -E.g. the letter `ö` is encoded with two bytes. The first of them may be at the end of one packet, and the second one -- at the beginning of the next packet. - -So, during the `readyState`, at the end of `responseText` there will be a half-character byte. That may lead to problems. In some simple cases, when we use only latin characters and digits (they all are encoded with 1 byte), such thing can't happen, but in other cases, that can become a source of bugs. -``` - -## HTTP-headers - -`XMLHttpRequest` allows both to send custom headers and read headers from the response. - -There are 3 methods for HTTP-headers: - -`setRequestHeader(name, value)` -: Sets the request header with the given `name` and `value`. - - For instance: - - ```js - xhr.setRequestHeader('Content-Type', 'application/json'); - ``` - - ```warn header="Headers limitations" - Several headers are managed exclusively by the browser, e.g. `Referer` and `Host`. - The full list is [in the specification](http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader-method). - - XMLHttpRequest is not allowed to change them, for the sake of user safety and correctness of the request. - ``` - - ````warn header="Can't remove a header" - Another peciliarity of `XMLHttpRequest` is that one can't undo `setRequestHeader`. - - Once the header is set, it's set. Additional calls add information to the header, don't overwrite it. - - For instance: - - ```js - xhr.setRequestHeader('X-Auth', '123'); - xhr.setRequestHeader('X-Auth', '456'); - - // the header will be: - // X-Auth: 123, 456 - ``` - ```` - -`getResponseHeader(name)` -: Gets the response header with the given `name` (except `Set-Cookie` and `Set-Cookie2`). - - For instance: - - ```js - xhr.getResponseHeader('Content-Type') - ``` - -`getAllResponseHeaders()` -: Returns all response headers, except `Set-Cookie` and `Set-Cookie2`. - - Headers are returned as a single line, e.g.: - - ``` - Cache-Control: max-age=31536000 - Content-Length: 4260 - Content-Type: image/png - Date: Sat, 08 Sep 2012 16:53:16 GMT - ``` - - The line break between headers is always `"\r\n"` (doesn't depend on OS), so we can easily split it into individual headers. The separator between the name and the value is always a colon followed by a space `": "`. That's fixed in the specification. - - So, if we want to get an object with name/value pairs, we need to throw in a bit JS. - - Like this (assuming that if two headers have the same name, then the latter one overwrites the former one): - - ```js - let headers = xhr - .getAllResponseHeaders() - .split('\r\n') - .reduce((result, current) => { - let [name, value] = current.split(': '); - result[name] = value; - return acc; - }, {}); - ``` - - -## Timeout - -The maximum duration of an asynchronous request can be set using the `timeout` property: - -```js -xhr.timeout = 30000; // 30 seconds (in milliseconds) -``` - -If the request exceeds that time, it's aborted, and the `timeout` event is generated: - -```js -xhr.ontimeout = function() { - alert( 'Sorry, the request took too long.' ); -} -``` - -## The full event list - -The [modern specification](http://www.w3.org/TR/XMLHttpRequest/#events) lists following events (in the lifecycle order): - -- `loadstart` -- the request has started. -- `progress` -- a data packet of the response has arrived, the whole response body at the moment is in `responseText`. -- `abort` -- the request was canceled by the call `xhr.abort()`. -- `error` -- connection error has occured, e.g. wrong domain name. Doesn't happen for HTTP-errors like 404. -- `load` -- the request has finished successfully. -- `timeout` -- the request was canceled due to timeout (only happens if it was set). -- `loadend` -- the request has finished (succeffully or not). - -The most used events are load completion (`onload`), load failure (`onerror`), and also `onprogress` to track the progress. - -We've already seen another event: `readystatechange`. Historically, it appeared long ago, before the specification settled. Nowadays, there's no need to use it, we can replace it with newer events, but it can often be found in older scripts. - -## Summary - -Typical code of the GET-request with `XMLHttpRequest`: - -```js -let xhr = new XMLHttpRequest(); - -xhr.open('GET', '/my/url'); - -xhr.send(); - -xhr.onload = function() { - // we can check - // status, statusText - for response HTTP status - // responseText, responseXML (when content-type: text/xml) - for the response - - if (this.status != 200) { - // handle error - alert( 'error: ' + this.status); - return; - } - - // get the response from this.responseText -}; - -xhr.onerror = function() { - // handle error -}; -``` - -XMLHttpRequest is widely used, but there's a more modern method named `fetch(url)` that returns a promise, thus working well with async/await. We'll cover it soon in the next sections. diff --git a/7-network/1-xmlhttprequest/readystate.view/index.html b/7-network/1-xmlhttprequest/readystate.view/index.html deleted file mode 100644 index d9f32ced9..000000000 --- a/7-network/1-xmlhttprequest/readystate.view/index.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - -
              - - - - - diff --git a/7-network/1-xmlhttprequest/readystate.view/server.js b/7-network/1-xmlhttprequest/readystate.view/server.js deleted file mode 100644 index 63b01a021..000000000 --- a/7-network/1-xmlhttprequest/readystate.view/server.js +++ /dev/null @@ -1,42 +0,0 @@ -var http = require('http'); -var url = require('url'); -var querystring = require('querystring'); -var static = require('node-static'); -var file = new static.Server('.'); - -function accept(req, res) { - - if (req.url == '/digits') { - - res.writeHead(200, { - 'Content-Type': 'text/plain', - 'Cache-Control': 'no-cache' - }); - - var i = 0; - - var timer = setInterval(write, 1000); - write(); - - function write() { - res.write(new Array(1000).join(++i + '') + ' '); - if (i == 9) { - clearInterval(timer); - res.end(); - } - - } - } else { - file.serve(req, res); - } -} - - - -// ----- запуск accept как сервера из консоли или как модуля ------ - -if (!module.parent) { - http.createServer(accept).listen(8080); -} else { - exports.accept = accept; -} \ No newline at end of file diff --git a/7-network/index.md b/7-network/index.md deleted file mode 100644 index 1ee6a3e55..000000000 --- a/7-network/index.md +++ /dev/null @@ -1,5 +0,0 @@ -development: true - ---- - -# Network requests: AJAX and COMET diff --git a/8-web-components/1-webcomponents-intro/article.md b/8-web-components/1-webcomponents-intro/article.md new file mode 100644 index 000000000..e3e175f49 --- /dev/null +++ b/8-web-components/1-webcomponents-intro/article.md @@ -0,0 +1,75 @@ +# From the orbital height + +This section describes a set of modern standards for "web components". + +As of now, these standards are under development. Some features are well-supported and integrated into the modern HTML/DOM standard, while others are yet in draft stage. You can try examples in any browser, Google Chrome is probably the most up to date with these features. Guess, that's because Google fellows are behind many of the related specifications. + +## What's common between... + +The whole component idea is nothing new. It's used in many frameworks and elsewhere. + +Before we move to implementation details, take a look at this great achievement of humanity: + +![](satellite.jpg) + +That's the International Space Station (ISS). + +And this is how it's made inside (approximately): + +![](satellite-expanded.jpg) + +The International Space Station: +- Consists of many components. +- Each component, in its turn, has many smaller details inside. +- The components are very complex, much more complicated than most websites. +- Components are developed internationally, by teams from different countries, speaking different languages. + +...And this thing flies, keeps humans alive in space! + +How such complex devices are created? + +Which principles we could borrow to make our development same-level reliable and scalable? Or, at least, close to it. + +## Component architecture + +The well known rule for developing complex software is: don't make complex software. + +If something becomes complex -- split it into simpler parts and connect in the most obvious way. + +**A good architect is the one who can make the complex simple.** + +We can split user interface into visual components: each of them has own place on the page, can "do" a well-described task, and is separate from the others. + +Let's take a look at a website, for example Twitter. + +It naturally splits into components: + +![](web-components-twitter.png) + +1. Top navigation. +2. User info. +3. Follow suggestions. +4. Submit form. +5. (and also 6, 7) -- messages. + +Components may have subcomponents, e.g. messages may be parts of a higher-level "message list" component. A clickable user picture itself may be a component, and so on. + +How do we decide, what is a component? That comes from intuition, experience and common sense. Usually it's a separate visual entity that we can describe in terms of what it does and how it interacts with the page. In the case above, the page has blocks, each of them plays its own role, it's logical to make these components. + +- A component has its own JavaScript class. +- DOM structure, managed solely by its class, outside code doesn't access it ("encapsulation" principle). +- CSS styles, applied to the component. +- API: events, class methods etc, to interact with other components. + +Once again, the whole "component" thing is nothing special. + +There exist many frameworks and development methodologies to build them, each one with its own bells and whistles. Usually, special CSS classes and conventions are used to provide "component feel" -- CSS scoping and DOM encapsulation. + +"Web components" provide built-in browser capabilities for that, so we don't have to emulate them any more. + +- [Custom elements](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements) -- to define custom HTML elements. +- [Shadow DOM](https://dom.spec.whatwg.org/#shadow-trees) -- to create an internal DOM for the component, hidden from the others. +- [CSS Scoping](https://drafts.csswg.org/css-scoping/) -- to declare styles that only apply inside the Shadow DOM of the component. +- [Event retargeting](https://dom.spec.whatwg.org/#retarget) and other minor stuff to make custom components better fit the development. + +In the next chapter we'll go into details of "Custom Elements" -- the fundamental and well-supported feature of web components, good on its own. diff --git a/8-web-components/1-webcomponents-intro/satellite-expanded.jpg b/8-web-components/1-webcomponents-intro/satellite-expanded.jpg new file mode 100644 index 000000000..56c127112 Binary files /dev/null and b/8-web-components/1-webcomponents-intro/satellite-expanded.jpg differ diff --git a/8-web-components/1-webcomponents-intro/satellite-expanded@2x.jpg b/8-web-components/1-webcomponents-intro/satellite-expanded@2x.jpg new file mode 100644 index 000000000..c65b12450 Binary files /dev/null and b/8-web-components/1-webcomponents-intro/satellite-expanded@2x.jpg differ diff --git a/8-web-components/1-webcomponents-intro/satellite.jpg b/8-web-components/1-webcomponents-intro/satellite.jpg new file mode 100644 index 000000000..9a3793f35 Binary files /dev/null and b/8-web-components/1-webcomponents-intro/satellite.jpg differ diff --git a/8-web-components/1-webcomponents-intro/satellite@2x.jpg b/8-web-components/1-webcomponents-intro/satellite@2x.jpg new file mode 100644 index 000000000..9624379d5 Binary files /dev/null and b/8-web-components/1-webcomponents-intro/satellite@2x.jpg differ diff --git a/8-web-components/1-webcomponents-intro/web-components-twitter.png b/8-web-components/1-webcomponents-intro/web-components-twitter.png new file mode 100644 index 000000000..9d59650c0 Binary files /dev/null and b/8-web-components/1-webcomponents-intro/web-components-twitter.png differ diff --git a/8-web-components/1-webcomponents-intro/web-components-twitter@2x.png b/8-web-components/1-webcomponents-intro/web-components-twitter@2x.png new file mode 100644 index 000000000..5abd4a2d8 Binary files /dev/null and b/8-web-components/1-webcomponents-intro/web-components-twitter@2x.png differ diff --git a/8-web-components/2-custom-elements/1-live-timer/solution.md b/8-web-components/2-custom-elements/1-live-timer/solution.md new file mode 100644 index 000000000..a9eacc880 --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/solution.md @@ -0,0 +1,4 @@ + +Please note: +1. We clear `setInterval` timer when the element is removed from the document. That's important, otherwise it continues ticking even if not needed any more. And the browser can't clear the memory from this element and referenced by it. +2. We can access current date as `elem.date` property. All class methods and properties are naturally element methods and properties. diff --git a/8-web-components/2-custom-elements/1-live-timer/solution.view/index.html b/8-web-components/2-custom-elements/1-live-timer/solution.view/index.html new file mode 100644 index 000000000..e1dd85096 --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/solution.view/index.html @@ -0,0 +1,9 @@ + + + + + + + diff --git a/8-web-components/2-custom-elements/1-live-timer/solution.view/live-timer.js b/8-web-components/2-custom-elements/1-live-timer/solution.view/live-timer.js new file mode 100644 index 000000000..a53d72e00 --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/solution.view/live-timer.js @@ -0,0 +1,32 @@ +class LiveTimer extends HTMLElement { + + render() { + this.innerHTML = ` + + + `; + + this.timerElem = this.firstElementChild; + } + + connectedCallback() { // (2) + if (!this.rendered) { + this.render(); + this.rendered = true; + } + this.timer = setInterval(() => this.update(), 1000); + } + + update() { + this.date = new Date(); + this.timerElem.setAttribute('datetime', this.date); + this.dispatchEvent(new CustomEvent('tick', { detail: this.date })); + } + + disconnectedCallback() { + clearInterval(this.timer); // important to let the element be garbage-collected + } + +} + +customElements.define("live-timer", LiveTimer); diff --git a/8-web-components/2-custom-elements/1-live-timer/solution.view/time-formatted.js b/8-web-components/2-custom-elements/1-live-timer/solution.view/time-formatted.js new file mode 100644 index 000000000..4fab99cb2 --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/solution.view/time-formatted.js @@ -0,0 +1,34 @@ +class TimeFormatted extends HTMLElement { + + render() { + let date = new Date(this.getAttribute('datetime') || Date.now()); + + this.innerHTML = new Intl.DateTimeFormat("default", { + year: this.getAttribute('year') || undefined, + month: this.getAttribute('month') || undefined, + day: this.getAttribute('day') || undefined, + hour: this.getAttribute('hour') || undefined, + minute: this.getAttribute('minute') || undefined, + second: this.getAttribute('second') || undefined, + timeZoneName: this.getAttribute('time-zone-name') || undefined, + }).format(date); + } + + connectedCallback() { + if (!this.rendered) { + this.render(); + this.rendered = true; + } + } + + static get observedAttributes() { + return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name']; + } + + attributeChangedCallback(name, oldValue, newValue) { + this.render(); + } + +} + +customElements.define("time-formatted", TimeFormatted); diff --git a/8-web-components/2-custom-elements/1-live-timer/source.view/index.html b/8-web-components/2-custom-elements/1-live-timer/source.view/index.html new file mode 100644 index 000000000..878120241 --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/source.view/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/8-web-components/2-custom-elements/1-live-timer/source.view/live-timer.js b/8-web-components/2-custom-elements/1-live-timer/source.view/live-timer.js new file mode 100644 index 000000000..e2fe2b69f --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/source.view/live-timer.js @@ -0,0 +1,7 @@ +class LiveTimer extends HTMLElement { + + /* your code here */ + +} + +customElements.define("live-timer", LiveTimer); diff --git a/8-web-components/2-custom-elements/1-live-timer/source.view/time-formatted.js b/8-web-components/2-custom-elements/1-live-timer/source.view/time-formatted.js new file mode 100644 index 000000000..4fab99cb2 --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/source.view/time-formatted.js @@ -0,0 +1,34 @@ +class TimeFormatted extends HTMLElement { + + render() { + let date = new Date(this.getAttribute('datetime') || Date.now()); + + this.innerHTML = new Intl.DateTimeFormat("default", { + year: this.getAttribute('year') || undefined, + month: this.getAttribute('month') || undefined, + day: this.getAttribute('day') || undefined, + hour: this.getAttribute('hour') || undefined, + minute: this.getAttribute('minute') || undefined, + second: this.getAttribute('second') || undefined, + timeZoneName: this.getAttribute('time-zone-name') || undefined, + }).format(date); + } + + connectedCallback() { + if (!this.rendered) { + this.render(); + this.rendered = true; + } + } + + static get observedAttributes() { + return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name']; + } + + attributeChangedCallback(name, oldValue, newValue) { + this.render(); + } + +} + +customElements.define("time-formatted", TimeFormatted); diff --git a/8-web-components/2-custom-elements/1-live-timer/task.md b/8-web-components/2-custom-elements/1-live-timer/task.md new file mode 100644 index 000000000..1feb7490a --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/task.md @@ -0,0 +1,23 @@ + +# Live timer element + +We already have `` element to show a nicely formatted time. + +Create `` element to show the current time: +1. It should use `` internally, not duplicate its functionality. +2. Ticks (updates) every second. +3. For every tick, a custom event named `tick` should be generated, with the current date in `event.detail` (see chapter ). + +Usage: + +```html + + + +``` + +Demo: + +[iframe src="solution" height=40] diff --git a/8-web-components/2-custom-elements/article.md b/8-web-components/2-custom-elements/article.md new file mode 100644 index 000000000..85434c75a --- /dev/null +++ b/8-web-components/2-custom-elements/article.md @@ -0,0 +1,399 @@ + +# Custom elements + +We can create custom HTML elements, described by our class, with its own methods and properties, events and so on. + +Once an custom element is defined, we can use it on par with built-in HTML elements. + +That's great, as HTML dictionary is rich, but not infinite. There are no ``, ``, ``... Just think of any other tag we might need. + +We can define them with a special class, and then use as if they were always a part of HTML. + +There are two kinds of custom elements: + +1. **Autonomous custom elements** -- "all-new" elements, extending the abstract `HTMLElement` class. +2. **Customized built-in elements** -- extending built-in elements, like customized `HTMLButtonElement` etc. + +First we'll create autonomous elements, and then customized built-in ones. + +To create a custom element, we need to tell the browser several details about it: how to show it, what to do when the element is added or removed to page, etc. + +That's done by making a class with special methods. That's easy, as there are only few methods, and all of them are optional. + +Here's a sketch with the full list: + +```js +class MyElement extends HTMLElement { + constructor() { + super(); + // element created + } + + connectedCallback() { + // browser calls it when the element is added to the document + // (can be called many times if an element is repeatedly added/removed) + } + + disconnectedCallback() { + // browser calls it when the element is removed from the document + // (can be called many times if an element is repeatedly added/removed) + } + + static get observedAttributes() { + return [/* array of attribute names to monitor for changes */]; + } + + attributeChangedCallback(name, oldValue, newValue) { + // called when one of attributes listed above is modified + } + + adoptedCallback() { + // called when the element is moved to a new document + // (happens in document.adoptNode, very rarely used) + } + + // there can be other element methods and properties +} +``` + +After that, we need to register the element: + +```js +// let the browser know that is served by our new class +customElements.define("my-element", MyElement); +``` + +Now for any HTML elements with tag ``, an instance of `MyElement` is created, and the aforementioned methods are called. We also can `document.createElement('my-element')` in JavaScript. + +```smart header="Custom element name must contain a hyphen `-`" +Custom element name must have a hyphen `-`, e.g. `my-element` and `super-button` are valid names, but `myelement` is not. + +That's to ensure that there are no name conflicts between built-in and custom HTML elements. +``` + +## Example: "time-formatted" + +For example, there already exists `