From c4a2fc16ea9dfa07724df75d8587469781c1a1c6 Mon Sep 17 00:00:00 2001 From: vplentinax Date: Sun, 10 May 2020 16:54:29 -0400 Subject: [PATCH 1/7] variable-scope --- .../1-closure-latest-changes/solution.md | 5 + .../1-closure-latest-changes/task.md | 23 + .../1-counter-independent/solution.md | 5 - .../_js.view/solution.js | 0 .../_js.view/source.js | 0 .../_js.view/test.js | 0 .../10-make-army/lexenv-makearmy.svg | 1 + .../03-closure/10-make-army/solution.md | 120 ++++ .../03-closure/10-make-army/task.md | 34 + .../lexenv-nested-work.svg | 1 + .../2-closure-variable-access/solution.md | 9 + .../2-closure-variable-access/task.md | 29 + .../3-counter-independent/solution.md | 5 + .../task.md | 7 +- .../03-closure/3-function-in-if/solution.md | 3 - .../03-closure/4-closure-sum/solution.md | 17 - .../03-closure/4-closure-sum/task.md | 17 - .../solution.md | 7 +- .../task.md | 8 +- .../03-closure/5-function-in-if/solution.md | 3 + .../task.md | 4 +- .../03-closure/6-closure-sum/solution.md | 16 + .../03-closure/6-closure-sum/task.md | 16 + .../6-filter-through-function/task.md | 29 - .../03-closure/7-let-scope/solution.md | 40 ++ .../03-closure/7-let-scope/task.md | 21 + .../_js.view/solution.js | 0 .../_js.view/source.js | 0 .../_js.view/test.js | 0 .../solution.md | 4 +- .../8-filter-through-function/task.md | 28 + .../8-make-army/lexenv-makearmy.svg | 93 --- .../03-closure/8-make-army/solution.md | 120 ---- .../03-closure/8-make-army/task.md | 35 - .../solution.md | 0 .../task.md | 16 +- .../03-closure/article.md | 630 ++++++------------ .../closure-function-declaration.svg | 1 + .../closure-makecounter-environment.svg | 1 + .../closure-makecounter-nested-call-2.svg | 1 + .../closure-makecounter-nested-call.svg | 1 + .../03-closure/closure-makecounter.svg | 1 + .../03-closure/closure-variable-phrase.svg | 1 + .../03-closure/lexenv-if.svg | 70 +- .../lexenv-nested-makecounter-1.svg | 40 +- .../lexenv-nested-makecounter-2.svg | 70 +- .../lexenv-nested-makecounter-3.svg | 72 +- .../lexenv-nested-makecounter-4.svg | 87 +-- .../lexenv-nested-makecounter-5.svg | 96 +-- .../lexenv-nested-makecounter-6.svg | 86 +-- .../03-closure/lexenv-nested-work.svg | 90 --- .../lexical-environment-global-2.svg | 65 +- .../lexical-environment-global-3.svg | 62 +- .../03-closure/lexical-environment-global.svg | 41 +- .../lexical-environment-simple-lookup.svg | 74 +- .../03-closure/lexical-environment-simple.svg | 68 +- .../03-closure/lexical-search-order.svg | 49 +- .../03-closure/variable-scope-lookup.svg | 1 + 58 files changed, 584 insertions(+), 1739 deletions(-) create mode 100644 1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md create mode 100644 1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md delete mode 100644 1-js/06-advanced-functions/03-closure/1-counter-independent/solution.md rename 1-js/06-advanced-functions/03-closure/{8-make-army => 10-make-army}/_js.view/solution.js (100%) rename 1-js/06-advanced-functions/03-closure/{8-make-army => 10-make-army}/_js.view/source.js (100%) rename 1-js/06-advanced-functions/03-closure/{8-make-army => 10-make-army}/_js.view/test.js (100%) create mode 100644 1-js/06-advanced-functions/03-closure/10-make-army/lexenv-makearmy.svg create mode 100644 1-js/06-advanced-functions/03-closure/10-make-army/solution.md create mode 100644 1-js/06-advanced-functions/03-closure/10-make-army/task.md create mode 100644 1-js/06-advanced-functions/03-closure/2-closure-variable-access/lexenv-nested-work.svg create mode 100644 1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md create mode 100644 1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md create mode 100644 1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md rename 1-js/06-advanced-functions/03-closure/{1-counter-independent => 3-counter-independent}/task.md (57%) delete mode 100644 1-js/06-advanced-functions/03-closure/3-function-in-if/solution.md delete mode 100644 1-js/06-advanced-functions/03-closure/4-closure-sum/solution.md delete mode 100644 1-js/06-advanced-functions/03-closure/4-closure-sum/task.md rename 1-js/06-advanced-functions/03-closure/{2-counter-object-independent => 4-counter-object-independent}/solution.md (62%) rename 1-js/06-advanced-functions/03-closure/{2-counter-object-independent => 4-counter-object-independent}/task.md (70%) create mode 100644 1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md rename 1-js/06-advanced-functions/03-closure/{3-function-in-if => 5-function-in-if}/task.md (61%) create mode 100644 1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md create mode 100644 1-js/06-advanced-functions/03-closure/6-closure-sum/task.md delete mode 100644 1-js/06-advanced-functions/03-closure/6-filter-through-function/task.md create mode 100644 1-js/06-advanced-functions/03-closure/7-let-scope/solution.md create mode 100644 1-js/06-advanced-functions/03-closure/7-let-scope/task.md rename 1-js/06-advanced-functions/03-closure/{6-filter-through-function => 8-filter-through-function}/_js.view/solution.js (100%) rename 1-js/06-advanced-functions/03-closure/{6-filter-through-function => 8-filter-through-function}/_js.view/source.js (100%) rename 1-js/06-advanced-functions/03-closure/{6-filter-through-function => 8-filter-through-function}/_js.view/test.js (100%) rename 1-js/06-advanced-functions/03-closure/{6-filter-through-function => 8-filter-through-function}/solution.md (90%) create mode 100644 1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md delete mode 100644 1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.svg delete mode 100644 1-js/06-advanced-functions/03-closure/8-make-army/solution.md delete mode 100644 1-js/06-advanced-functions/03-closure/8-make-army/task.md rename 1-js/06-advanced-functions/03-closure/{7-sort-by-field => 9-sort-by-field}/solution.md (100%) rename 1-js/06-advanced-functions/03-closure/{7-sort-by-field => 9-sort-by-field}/task.md (51%) create mode 100644 1-js/06-advanced-functions/03-closure/closure-function-declaration.svg create mode 100644 1-js/06-advanced-functions/03-closure/closure-makecounter-environment.svg create mode 100644 1-js/06-advanced-functions/03-closure/closure-makecounter-nested-call-2.svg create mode 100644 1-js/06-advanced-functions/03-closure/closure-makecounter-nested-call.svg create mode 100644 1-js/06-advanced-functions/03-closure/closure-makecounter.svg create mode 100644 1-js/06-advanced-functions/03-closure/closure-variable-phrase.svg delete mode 100644 1-js/06-advanced-functions/03-closure/lexenv-nested-work.svg create mode 100644 1-js/06-advanced-functions/03-closure/variable-scope-lookup.svg diff --git a/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md new file mode 100644 index 000000000..717c11728 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md @@ -0,0 +1,5 @@ +La respuesta es: **Pete**. + +Una función obtiene variables externas con su estado actual, y utiliza los valores más recientes. + +Los valores de variables anteriores no se guardan en ningún lado. Cuando una función quiere una variable, toma el valor actual de su propio entorno léxico o el externo. diff --git a/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md new file mode 100644 index 000000000..f3aadbbff --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md @@ -0,0 +1,23 @@ +importance: 5 + +--- + +# ¿Una función recoge los últimos cambios? + +La función sayHi usa un nombre de variable externo. Cuando se ejecuta la función, ¿qué valor va a utilizar? + +```js +let name = "John"; + +function sayHi() { + alert("Hi, " + name); +} + +name = "Pete"; + +sayHi(); // ¿qué mostrará: "John" o "Pete"? + +``` +Tales situaciones son comunes tanto en el desarrollo del navegador como del lado del servidor. Se puede programar que una función se ejecute más tarde de lo que se creó, por ejemplo, después de una acción del usuario o una solicitud de red. + +Entonces, la pregunta es: ¿recoge los últimos cambios? diff --git a/1-js/06-advanced-functions/03-closure/1-counter-independent/solution.md b/1-js/06-advanced-functions/03-closure/1-counter-independent/solution.md deleted file mode 100644 index 25ecbea4c..000000000 --- a/1-js/06-advanced-functions/03-closure/1-counter-independent/solution.md +++ /dev/null @@ -1,5 +0,0 @@ -The answer: **0,1.** - -Functions `counter` and `counter2` are created by different invocations of `makeCounter`. - -So they have independent outer Lexical Environments, each one has its own `count`. diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/_js.view/solution.js b/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/solution.js similarity index 100% rename from 1-js/06-advanced-functions/03-closure/8-make-army/_js.view/solution.js rename to 1-js/06-advanced-functions/03-closure/10-make-army/_js.view/solution.js diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/_js.view/source.js b/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/source.js similarity index 100% rename from 1-js/06-advanced-functions/03-closure/8-make-army/_js.view/source.js rename to 1-js/06-advanced-functions/03-closure/10-make-army/_js.view/source.js diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/_js.view/test.js b/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/test.js similarity index 100% rename from 1-js/06-advanced-functions/03-closure/8-make-army/_js.view/test.js rename to 1-js/06-advanced-functions/03-closure/10-make-army/_js.view/test.js diff --git a/1-js/06-advanced-functions/03-closure/10-make-army/lexenv-makearmy.svg b/1-js/06-advanced-functions/03-closure/10-make-army/lexenv-makearmy.svg new file mode 100644 index 000000000..c0a312ec7 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/10-make-army/lexenv-makearmy.svg @@ -0,0 +1 @@ +outeri: 0i: 1i: 2i: 10...makeArmy() LexicalEnvironmentfor block LexicalEnvironment \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/10-make-army/solution.md b/1-js/06-advanced-functions/03-closure/10-make-army/solution.md new file mode 100644 index 000000000..9b031e63b --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/10-make-army/solution.md @@ -0,0 +1,120 @@ + +Examinemos lo que se hace dentro de `makeArmy`, y la solución será obvia. + +1. Crea un array vacío. `shooters`: + + ```js + let shooters = []; + ``` +2. Lo llena en el bucle a través de `shooters.push(function...)`. + + Cada elemento es una función, por lo que el array resultante se ve así: + + ```js no-beautify + shooters = [ + function () { alert(i); }, + function () { alert(i); }, + function () { alert(i); }, + function () { alert(i); }, + function () { alert(i); }, + function () { alert(i); }, + function () { alert(i); }, + function () { alert(i); }, + function () { alert(i); }, + function () { alert(i); } + ]; + ``` + +3. El array se devuelve desde la función. + +Luego, más tarde, la llamada a `army[5] ()` obtendrá el elemento `army[5]` de el array (será una función) y lo llamará. + +Ahora, ¿por qué todas esas funciones muestran lo mismo? + +Esto se debe a que no hay una variable local `i` dentro de las funciones `shooter`. Cuando se llama a tal función, toma `i` de su entorno léxico externo. + +¿Cuál será el valor de 'i'? + +Si miramos la fuente: + +```js +function makeArmy() { + ... + let i = 0; + while (i < 10) { + let shooter = function() { // shooter function + alert( i ); // debería mostrar su número + }; + ... + } + ... +} +``` + +... Podemos ver que vive en el entorno léxico asociado con la ejecución actual de `makeArmy()`. Pero cuando se llama a `army[5]()`, `makeArmy` ya ha terminado su trabajo, y `i` tiene el último valor: `10` (el final de `while`). + +Como resultado, todas las funciones `shooter` obtienen del mismo entorno léxico externo, el último valor `i = 10`. + +Podemos arreglarlo moviendo la definición de variable al bucle: + +```js run demo +function makeArmy() { + + let shooters = []; + +*!* + for(let i = 0; i < 10; i++) { +*/!* + let shooter = function() { // shooter function + alert( i ); // debería mostrar su número + }; + shooters.push(shooter); + } + + return shooters; +} + +let army = makeArmy(); + +army[0](); // 0 +army[5](); // 5 +``` + +Ahora funciona correctamente, porque cada vez que se ejecuta el bloque de código en `for (let i = 0 ...) {...}`, se crea un nuevo entorno léxico para él, con la variable correspondiente `i`. + +Entonces, el valor de `i` ahora vive un poco más cerca. No en el entorno léxico `makeArmy()`, sino en el entorno léxico que corresponde a la iteración del bucle actual. Por eso ahora funciona. + +![](lexenv-makearmy.svg) + +Aquí reescribimos `while` en `for`. + +Podría usarse otro truco, veámoslo para comprender mejor el tema: + +```js run +function makeArmy() { + let shooters = []; + + let i = 0; + while (i < 10) { +*!* + let j = i; +*/!* + let shooter = function() { // shooter function + alert( *!*j*/!* ); // debería verse el núemero + }; + shooters.push(shooter); + i++; + } + + return shooters; +} + +let army = makeArmy(); + +army[0](); // 0 +army[5](); // 5 +``` + +El bucle `while`, al igual que `for`, crea un nuevo entorno léxico para cada ejecución. Así que aquí nos aseguramos de que obtenga el valor correcto para un `shooter`. + +Copiamos `let j = i`. Esto hace que el cuerpo del bucle sea `j` local y copia el valor de `i` en él. Los primitivos se copian "por valor", por lo que en realidad obtenemos una copia independiente completa de `i`, que pertenece a la iteración del bucle actual. diff --git a/1-js/06-advanced-functions/03-closure/10-make-army/task.md b/1-js/06-advanced-functions/03-closure/10-make-army/task.md new file mode 100644 index 000000000..fe0aa9f5d --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/10-make-army/task.md @@ -0,0 +1,34 @@ +importance: 5 + +--- + +# Ejército de funciones + +El siguiente código crea una serie de `shooters`. + +Cada función está destinada a generar su número. Pero algo anda mal ... + +```js run +function makeArmy() { + let shooters = []; + + let i = 0; + while (i < 10) { + let shooter = function() { // shooter function + alert( i ); // debería mostrar su número + }; + shooters.push(shooter); + i++; + } + + return shooters; +} + +let army = makeArmy(); + +army[0](); // el tirador número 0 muestra 10 +army[5](); //y el número 5 también produce 10... +// ... todos los tiradores muestran 10 en lugar de sus 0, 1, 2, 3 ... +``` + +¿Por qué todos los tiradores muestran el mismo valor? Arregle el código para que funcionen según lo previsto. diff --git a/1-js/06-advanced-functions/03-closure/2-closure-variable-access/lexenv-nested-work.svg b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/lexenv-nested-work.svg new file mode 100644 index 000000000..5cdf7f1a4 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/lexenv-nested-work.svg @@ -0,0 +1 @@ +makeWorker: function name: "John"<empty>outerouterouternullname: "Pete" \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md new file mode 100644 index 000000000..12af43a67 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md @@ -0,0 +1,9 @@ +La respuesta es: **Pete**. + +La función `work()` en el código a continuación obtiene `name` del lugar de su origen a través de la referencia del entorno léxico externo: + +![](lexenv-nested-work.svg) + +Entonces, el resultado es "Pete". + +Pero si no hubiera `let name` en` makeWorker () `, entonces la búsqueda saldría y tomaría la variable global como podemos ver en la cadena de arriba. En ese caso, el resultado sería `John`. diff --git a/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md new file mode 100644 index 000000000..d19f17e4b --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md @@ -0,0 +1,29 @@ +importance: 5 + +--- + +# ¿Qué variables están disponibles? + +La función `makeWorker` a continuación crea otra función y la devuelve. Esa nueva función se puede llamar desde otro lugar. + +¿Tendrá acceso a las variables externas desde su lugar de creación, o desde el lugar de invocación, o ambos? + +```js +function makeWorker() { + let name = "Pete"; + + return function() { + alert(name); + }; +} + +let name = "John"; + +// crea una funcion +let work = makeWorker(); + +// la llama +work(); // ¿qué mostrará? +``` + +¿Qué valor mostrará? "Pete" o "John"? diff --git a/1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md b/1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md new file mode 100644 index 000000000..5c42b9ddd --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md @@ -0,0 +1,5 @@ +La respuesta: **0,1.** + +Las funciones `counter` y` counter2` son creadas por diferentes invocaciones de `makeCounter`. + +Por lo tanto, tienen entornos léxicos externos independientes, cada uno tiene su propio `count`. diff --git a/1-js/06-advanced-functions/03-closure/1-counter-independent/task.md b/1-js/06-advanced-functions/03-closure/3-counter-independent/task.md similarity index 57% rename from 1-js/06-advanced-functions/03-closure/1-counter-independent/task.md rename to 1-js/06-advanced-functions/03-closure/3-counter-independent/task.md index e8c17dd31..9eda6411e 100644 --- a/1-js/06-advanced-functions/03-closure/1-counter-independent/task.md +++ b/1-js/06-advanced-functions/03-closure/3-counter-independent/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Are counters independent? +# ¿Son independientes los contadores? -Here we make two counters: `counter` and `counter2` using the same `makeCounter` function. +Aquí hacemos dos contadores: `counter` y `counter2` usando la misma función `makeCounter`. -Are they independent? What is the second counter going to show? `0,1` or `2,3` or something else? +¿Son independientes? ¿Qué va a mostrar el segundo contador? `0,1` o `2,3` o algo más? ```js function makeCounter() { @@ -28,4 +28,3 @@ alert( counter2() ); // ? alert( counter2() ); // ? */!* ``` - diff --git a/1-js/06-advanced-functions/03-closure/3-function-in-if/solution.md b/1-js/06-advanced-functions/03-closure/3-function-in-if/solution.md deleted file mode 100644 index e2e7a91b3..000000000 --- a/1-js/06-advanced-functions/03-closure/3-function-in-if/solution.md +++ /dev/null @@ -1,3 +0,0 @@ -The result is **an error**. - -The function `sayHi` is declared inside the `if`, so it only lives inside it. There is no `sayHi` outside. \ No newline at end of file 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 deleted file mode 100644 index a6679cd20..000000000 --- a/1-js/06-advanced-functions/03-closure/4-closure-sum/solution.md +++ /dev/null @@ -1,17 +0,0 @@ -For the second parentheses to work, the first ones must return a function. - -Like this: - -```js run -function sum(a) { - - return function(b) { - return a + b; // takes "a" from the outer lexical environment - }; - -} - -alert( sum(1)(2) ); // 3 -alert( sum(5)(-1) ); // 4 -``` - 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 deleted file mode 100644 index b45758562..000000000 --- a/1-js/06-advanced-functions/03-closure/4-closure-sum/task.md +++ /dev/null @@ -1,17 +0,0 @@ -importance: 4 - ---- - -# Sum with closures - -Write function `sum` that works like this: `sum(a)(b) = a+b`. - -Yes, exactly this way, using double parentheses (not a mistype). - -For instance: - -```js -sum(1)(2) = 3 -sum(5)(-1) = 4 -``` - diff --git a/1-js/06-advanced-functions/03-closure/2-counter-object-independent/solution.md b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md similarity index 62% rename from 1-js/06-advanced-functions/03-closure/2-counter-object-independent/solution.md rename to 1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md index cd4e641e4..6b1910f7b 100644 --- a/1-js/06-advanced-functions/03-closure/2-counter-object-independent/solution.md +++ b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md @@ -1,7 +1,6 @@ +Seguramente funcionará bien. -Surely it will work just fine. - -Both nested functions are created within the same outer Lexical Environment, so they share access to the same `count` variable: +Ambas funciones anidadas se crean dentro del mismo entorno léxico externo, por lo que comparten acceso a la misma variable `count`: ```js run function Counter() { @@ -10,7 +9,7 @@ function Counter() { this.up = function() { return ++count; }; - + this.down = function() { return --count; }; diff --git a/1-js/06-advanced-functions/03-closure/2-counter-object-independent/task.md b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md similarity index 70% rename from 1-js/06-advanced-functions/03-closure/2-counter-object-independent/task.md rename to 1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md index d770b0ffc..046ec0ba5 100644 --- a/1-js/06-advanced-functions/03-closure/2-counter-object-independent/task.md +++ b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md @@ -2,11 +2,12 @@ importance: 5 --- -# Counter object +# Objeto contador -Here a counter object is made with the help of the constructor function. +Aquí se crea un objeto contador con la ayuda de la función constructora. + +¿Funcionará? ¿Qué mostrará? -Will it work? What will it show? ```js function Counter() { @@ -26,4 +27,3 @@ alert( counter.up() ); // ? alert( counter.up() ); // ? alert( counter.down() ); // ? ``` - diff --git a/1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md b/1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md new file mode 100644 index 000000000..1f51fe2b4 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md @@ -0,0 +1,3 @@ +El resultado es **un error**. + +La función `sayHi` se declara dentro de `if`, por lo que solo vive dentro de ella. No hay `sayHi` afuera. diff --git a/1-js/06-advanced-functions/03-closure/3-function-in-if/task.md b/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md similarity index 61% rename from 1-js/06-advanced-functions/03-closure/3-function-in-if/task.md rename to 1-js/06-advanced-functions/03-closure/5-function-in-if/task.md index d0dbbeb11..9480e2487 100644 --- a/1-js/06-advanced-functions/03-closure/3-function-in-if/task.md +++ b/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md @@ -1,7 +1,7 @@ -# Function in if +# Función en if -Look at the code. What will be result of the call at the last line? +Mira el código ¿Cuál será el resultado de la llamada en la última línea? ```js run let phrase = "Hello"; diff --git a/1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md b/1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md new file mode 100644 index 000000000..9a975dae7 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md @@ -0,0 +1,16 @@ +Para que funcionen los segundos paréntesis, los primeros deben devolver una función. + +Como esto: + +```js run +function sum(a) { + + return function(b) { + return a + b; // toma "a" del entorno léxico externo + }; + +} + +alert( sum(1)(2) ); // 3 +alert( sum(5)(-1) ); // 4 +``` diff --git a/1-js/06-advanced-functions/03-closure/6-closure-sum/task.md b/1-js/06-advanced-functions/03-closure/6-closure-sum/task.md new file mode 100644 index 000000000..6d3e1153f --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/6-closure-sum/task.md @@ -0,0 +1,16 @@ +importance: 4 + +--- + +# Suma con cierres + +Escriba la función `sum` que funcione así: `sum(a)(b) = a+b`. + +Sí, exactamente de esta manera, usando paréntesis dobles (no un tipo incorrecto). + +Por ejemeplo: + +```js +sum(1)(2) = 3 +sum(5)(-1) = 4 +``` diff --git a/1-js/06-advanced-functions/03-closure/6-filter-through-function/task.md b/1-js/06-advanced-functions/03-closure/6-filter-through-function/task.md deleted file mode 100644 index d1c39f949..000000000 --- a/1-js/06-advanced-functions/03-closure/6-filter-through-function/task.md +++ /dev/null @@ -1,29 +0,0 @@ -importance: 5 - ---- - -# Filter through function - -We have a built-in method `arr.filter(f)` for arrays. It filters all elements through the function `f`. If it returns `true`, then that element is returned in the resulting array. - -Make a set of "ready to use" filters: - -- `inBetween(a, b)` -- between `a` and `b` or equal to them (inclusively). -- `inArray([...])` -- in the given array. - -The usage must be like this: - -- `arr.filter(inBetween(3,6))` -- selects only values between 3 and 6. -- `arr.filter(inArray([1,2,3]))` -- selects only elements matching with one of the members of `[1,2,3]`. - -For instance: - -```js -/* .. your code for inBetween and inArray */ -let arr = [1, 2, 3, 4, 5, 6, 7]; - -alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6 - -alert( arr.filter(inArray([1, 2, 10])) ); // 1,2 -``` - diff --git a/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md b/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md new file mode 100644 index 000000000..7b9b89a40 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md @@ -0,0 +1,40 @@ +El resultado es: **error**. + +Intenta correr esto: + +```js run +let x = 1; + +function func() { +*!* + console.log(x); // ReferenceError: No se puede acceder a 'x' antes de la inicialización +*/!* + let x = 2; +} + +func(); +``` + +En este ejemplo podemos observar la diferencia peculiar entre una variable "no existente" y una variable "no inicializada". + +Como habrás leído en el artículo [](info:closure), una variable comienza en el estado "no inicializado" desde el momento en que la ejecución entra en un bloque de código (o una función). Y permanece sin inicializar hasta la correspondiente declaración `let`. + +En otras palabras, una variable técnicamente existe, pero no se puede usar antes de `let`. + +El código anterior lo demuestra. + +```js +function func() { +*!* +// la variable local x es conocida por el motor desde el comienzo de la función, +// pero "unitialized" (inutilizable) hasta let ("zona muerta") +// de ahí el error +*/!* + + console.log(x); // ReferenceError: No se puede acceder a 'x' antes de la inicialización + + let x = 2; +} +``` + +Esta zona de inutilización temporal de una variable (desde el comienzo del bloque de código hasta `let`) a veces se denomina" zona muerta ". diff --git a/1-js/06-advanced-functions/03-closure/7-let-scope/task.md b/1-js/06-advanced-functions/03-closure/7-let-scope/task.md new file mode 100644 index 000000000..b38550e9f --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/7-let-scope/task.md @@ -0,0 +1,21 @@ +importance: 4 + +--- + +# ¿Es visible la variable? + +¿Cuál será el resultado de este código? + +```js +let x = 1; + +function func() { + console.log(x); // ? + + let x = 2; +} + +func(); +``` + +P.D Hay una trampa en esta tarea. La solución no es obvia. diff --git a/1-js/06-advanced-functions/03-closure/6-filter-through-function/_js.view/solution.js b/1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/solution.js similarity index 100% rename from 1-js/06-advanced-functions/03-closure/6-filter-through-function/_js.view/solution.js rename to 1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/solution.js diff --git a/1-js/06-advanced-functions/03-closure/6-filter-through-function/_js.view/source.js b/1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/source.js similarity index 100% rename from 1-js/06-advanced-functions/03-closure/6-filter-through-function/_js.view/source.js rename to 1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/source.js diff --git a/1-js/06-advanced-functions/03-closure/6-filter-through-function/_js.view/test.js b/1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/test.js similarity index 100% rename from 1-js/06-advanced-functions/03-closure/6-filter-through-function/_js.view/test.js rename to 1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/test.js diff --git a/1-js/06-advanced-functions/03-closure/6-filter-through-function/solution.md b/1-js/06-advanced-functions/03-closure/8-filter-through-function/solution.md similarity index 90% rename from 1-js/06-advanced-functions/03-closure/6-filter-through-function/solution.md rename to 1-js/06-advanced-functions/03-closure/8-filter-through-function/solution.md index 46c5514a8..15824d518 100644 --- a/1-js/06-advanced-functions/03-closure/6-filter-through-function/solution.md +++ b/1-js/06-advanced-functions/03-closure/8-filter-through-function/solution.md @@ -1,5 +1,5 @@ -# Filter inBetween +# Filtrar inBetween ```js run function inBetween(a, b) { @@ -12,7 +12,7 @@ let arr = [1, 2, 3, 4, 5, 6, 7]; alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6 ``` -# Filter inArray +# Filtrar inArray ```js run demo function inArray(arr) { diff --git a/1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md b/1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md new file mode 100644 index 000000000..50006931d --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md @@ -0,0 +1,28 @@ +importance: 5 + +--- + +# Filtrar a través de una función + +Tenemos un método incorporado `arr.filter(f)` para arrays. Filtra todos los elementos a través de la función `f`. Si devuelve `true`, entonces ese elemento se devuelve en el array resultante. + +Haga un conjunto de filtros "listos para usar": + +- `inBetween(a, b)` -- entre `a` y `b` o igual a ellos (inclusive). (inclusively). +- `inArray([...])` -- en el array dado + +El uso debe ser así: + +- `arr.filter(inBetween(3,6))` -- selecciona solo valores entre 3 y 6. +- `arr.filter(inArray([1,2,3]))` -- selecciona solo elementos que coinciden con uno de los miembros de `[1,2,3]`. + +Por ejemplo: + +```js +/* .. tu código para inBetween y inArray */ +let arr = [1, 2, 3, 4, 5, 6, 7]; + +alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6 + +alert( arr.filter(inArray([1, 2, 10])) ); // 1,2 +``` diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.svg b/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.svg deleted file mode 100644 index aa5e2d6e0..000000000 --- a/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - lexenv-makearmy.svg - Created with sketchtool. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - outer - - - - - - - - - i: 0 - - - - - - - - - i: 1 - - - - - - - - - i: 2 - - - - - - - - - i: 10 - - - - ... - - - makeArmy() - Lexical Environment - - - for block - Lexical Environment - - - - \ No newline at end of file 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 deleted file mode 100644 index 0fb0b4a49..000000000 --- a/1-js/06-advanced-functions/03-closure/8-make-army/solution.md +++ /dev/null @@ -1,120 +0,0 @@ - -Let's examine what's done inside `makeArmy`, and the solution will become obvious. - -1. It creates an empty array `shooters`: - - ```js - let shooters = []; - ``` -2. Fills it in the loop via `shooters.push(function...)`. - - Every element is a function, so the resulting array looks like this: - - ```js no-beautify - shooters = [ - function () { alert(i); }, - function () { alert(i); }, - function () { alert(i); }, - function () { alert(i); }, - function () { alert(i); }, - function () { alert(i); }, - function () { alert(i); }, - function () { alert(i); }, - function () { alert(i); }, - function () { alert(i); } - ]; - ``` - -3. The array is returned from the function. - -Then, later, the call to `army[5]()` will get the element `army[5]` from the array (it will be a function) and call it. - -Now why all such functions show the same? - -That's because there's no local variable `i` inside `shooter` functions. When such a function is called, it takes `i` from its outer lexical environment. - -What will be the value of `i`? - -If we look at the source: - -```js -function makeArmy() { - ... - let i = 0; - while (i < 10) { - let shooter = function() { // shooter function - alert( i ); // should show its number - }; - ... - } - ... -} -``` - -...We can see that it lives in the lexical environment associated with the current `makeArmy()` run. But when `army[5]()` is called, `makeArmy` has already finished its job, and `i` has the last value: `10` (the end of `while`). - -As a result, all `shooter` functions get from the outer lexical envrironment the same, last value `i=10`. - -We can fix it by moving the variable definition into the loop: - -```js run demo -function makeArmy() { - - let shooters = []; - -*!* - for(let i = 0; i < 10; i++) { -*/!* - let shooter = function() { // shooter function - alert( i ); // should show its number - }; - shooters.push(shooter); - } - - return shooters; -} - -let army = makeArmy(); - -army[0](); // 0 -army[5](); // 5 -``` - -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. - -![](lexenv-makearmy.svg) - -Here we rewrote `while` into `for`. - -Another trick could be possible, let's see it for better understanding of the subject: - -```js run -function makeArmy() { - let shooters = []; - - let i = 0; - while (i < 10) { -*!* - let j = i; -*/!* - let shooter = function() { // shooter function - alert( *!*j*/!* ); // should show its number - }; - shooters.push(shooter); - i++; - } - - return shooters; -} - -let army = makeArmy(); - -army[0](); // 0 -army[5](); // 5 -``` - -The `while` loop, just like `for`, makes a new Lexical Environment for each run. So here we make sure that it gets the right value for a `shooter`. - -We copy `let j = i`. This makes a loop body local `j` and copies the value of `i` to it. Primitives are copied "by value", so we actually get a complete independent copy of `i`, belonging to the current loop iteration. diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/task.md b/1-js/06-advanced-functions/03-closure/8-make-army/task.md deleted file mode 100644 index ede8fd045..000000000 --- a/1-js/06-advanced-functions/03-closure/8-make-army/task.md +++ /dev/null @@ -1,35 +0,0 @@ -importance: 5 - ---- - -# Army of functions - -The following code creates an array of `shooters`. - -Every function is meant to output its number. But something is wrong... - -```js run -function makeArmy() { - let shooters = []; - - let i = 0; - while (i < 10) { - let shooter = function() { // shooter function - alert( i ); // should show its number - }; - shooters.push(shooter); - i++; - } - - return shooters; -} - -let army = makeArmy(); - -army[0](); // the shooter number 0 shows 10 -army[5](); // and number 5 also outputs 10... -// ... all shooters show 10 instead of their 0, 1, 2, 3... -``` - -Why all shooters show the same? Fix the code so that they work as intended. - diff --git a/1-js/06-advanced-functions/03-closure/7-sort-by-field/solution.md b/1-js/06-advanced-functions/03-closure/9-sort-by-field/solution.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/7-sort-by-field/solution.md rename to 1-js/06-advanced-functions/03-closure/9-sort-by-field/solution.md diff --git a/1-js/06-advanced-functions/03-closure/7-sort-by-field/task.md b/1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md similarity index 51% rename from 1-js/06-advanced-functions/03-closure/7-sort-by-field/task.md rename to 1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md index 08fb5cc34..a89f4d8d0 100644 --- a/1-js/06-advanced-functions/03-closure/7-sort-by-field/task.md +++ b/1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Sort by field +# Ordenar por campo -We've got an array of objects to sort: +Tenemos una variedad de objetos para ordenar: ```js let users = [ @@ -14,23 +14,23 @@ let users = [ ]; ``` -The usual way to do that would be: +La forma habitual de hacerlo sería: ```js -// by name (Ann, John, Pete) +// por nombre(Ann, John, Pete) users.sort((a, b) => a.name > b.name ? 1 : -1); -// by age (Pete, Ann, John) +// por edad (Pete, Ann, John) users.sort((a, b) => a.age > b.age ? 1 : -1); ``` -Can we make it even less verbose, like this? +¿Podemos hacerlo aún menos detallado, como este? ```js users.sort(byField('name')); users.sort(byField('age')); ``` -So, instead of writing a function, just put `byField(fieldName)`. +Entonces, en lugar de escribir una función, simplemente ponga `byField (fieldName)`. -Write the function `byField` that can be used for that. +Escriba la función `byField` que se pueda usar para eso. diff --git a/1-js/06-advanced-functions/03-closure/article.md b/1-js/06-advanced-functions/03-closure/article.md index 55e3a7149..08e3ced52 100644 --- a/1-js/06-advanced-functions/03-closure/article.md +++ b/1-js/06-advanced-functions/03-closure/article.md @@ -1,216 +1,111 @@ -# Closure +# Ámbito de Variable -JavaScript is a very function-oriented language. It gives us a lot of freedom. A function can be created at one moment, then copied to another variable or passed as an argument to another function and called from a totally different place later. +JavaScript es un lenguaje muy orientado a funciones. Nos da mucha libertad. Se puede crear una función dinámicamente, pasarla como argumento a otra función y llamarla desde un lugar de código totalmente diferente más adelante. -We know that a function can access variables outside of it; this feature is used quite often. +Ya sabemos que una función puede acceder a variables fuera de ella. -But what happens when an outer variable changes? Does a function get the most recent value or the one that existed when the function was created? +Ahora ampliemos nuestro conocimiento para incluir escenarios más complejos. -Also, what happens when a function travels to another place in the code and is called from there -- does it get access to the outer variables of the new place? +```smart header="Hablaremos de las variables `let / const` aquí" +En JavaScript, hay 3 formas de declarar una variable: `let`, `const` (las modernas) y `var` (más antigua). -Different languages behave differently here, and in this chapter we cover the behaviour of JavaScript. - -## A couple of questions - -Let's consider two situations to begin with, and then study the internal mechanics piece-by-piece, so that you'll be able to answer the following questions and more complex ones in the future. - -1. The function `sayHi` uses an external variable `name`. When the function runs, which value is it going to use? - - ```js - let name = "John"; - - function sayHi() { - alert("Hi, " + name); - } - - name = "Pete"; - - *!* - sayHi(); // what will it show: "John" or "Pete"? - */!* - ``` - - Such situations are common both in browser and server-side development. A function may be scheduled to execute later than it is created, for instance after a user action or a network request. - - So, the question is: does it pick up the latest changes? - - -2. The function `makeWorker` makes another function and returns it. That new function can be called from somewhere else. Will it have access to the outer variables from its creation place, or the invocation place, or both? - - ```js - function makeWorker() { - let name = "Pete"; - - return function() { - alert(name); - }; - } - - let name = "John"; - - // create a function - let work = makeWorker(); - - // call it - *!* - work(); // what will it show? "Pete" (name where created) or "John" (name where called)? - */!* - ``` - - -## Lexical Environment - -To understand what's going on, let's first discuss what a "variable" actually is. - -In JavaScript, every running function, code block, and the script as a whole have an associated object known as the *Lexical Environment*. - -The Lexical Environment object consists of two parts: - -1. *Environment Record* -- an object that has all local variables as its properties (and some other information like the value of `this`). -2. A reference to the *outer lexical environment*, usually the one associated with the code lexically right outside of it (outside of the current curly brackets). - -**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".** - -For instance, in this simple code, there is only one Lexical Environment: - -![lexical environment](lexical-environment-global.svg) - -This is a so-called global Lexical Environment, associated with the whole script. For browsers, all `