let y const JavaScript

Hola, estoy dándole caña al tema de let y const para declarar variables locales y constantes. Ya más o menos me defiendo pero con alguna dudilla todavía.

En el ejemplo que pongo a continuación que saque de un ejemplo explicativo la ultima variable (var MY_FAV = 20;) da un error en la consola y la explicación que pone es porque este se alza en el contexto global. No comprendo que quiere decir, a mi entender la contante MY_FAV = 20 que es local no chocaría con var MY_FAV = 20; que es global y no debería dar un error: SyntaxError: tratando de redeclarar una constante. El identificador 'MY_FAV' ya ha sido declarado.

<script>
// definimos MY_FAV como constante y le damos un valor de 7
const MY_FAV = 7;

// es importante tener en cuenta como funciona el alcance de bloque
if (MY_FAV === 7) {
// esto esta bien y crea una variable MY_FAV de alcance/ambito de bloque
// (funciona igual de bien con let para declarar un alcance de bloque /ambito de variable no-constante)
const MY_FAV = 20;

// MY_FAV ahora es 20
console.log('my favorite number is ' + MY_FAV);

// este se alza en el contexto global y lanza un error
var MY_FAV = 20;
}

// MY_FAV todavia es 7
console.log('my favorite number is ' + MY_FAV);
</script>
Hola!

Lo que pasa es que no puedes tener dos variables con el mismo nombre dentro de un solo contexto/bloque:

if (MY_FAV === 7) {
// esto esta bien y crea una variable MY_FAV de alcance/ambito de bloque
// (funciona igual de bien con let para declarar un alcance de bloque /ambito de variable no-constante)
const MY_FAV = 20; [COLOR="#FF8C00"]// Primera declaración; no hay problema.[/COLOR]

// MY_FAV ahora es 20
console.log('my favorite number is ' + MY_FAV);

// este se alza en el contexto global y lanza un error
var MY_FAV = 20; [COLOR="#FF8C00"]// Segunda declaración dentro del mismo contexto.[/COLOR]
[COLOR="#FF8C00"]// No podrías cambiar el valor de MY_FAV de ninguna manera, pues la declaraste como const en el contexto global y el local.[/COLOR]
}

Aquí MY_FAV aparece dos veces.
Una pregunta sobre este asunto.

Cuando hablamos de contexto léxico, queremos decir al contexto entre llaves y ámbito del bloque. ¿ es todo lo mismo? es que nose si puedo estar confundiendo terminos.
Si, es lo mismo. El contexto es todo a lo que tiene acceso la acción que está ejecutando el compilador (o intérprete) en un determinado momento (recuerda que el compilador o intérprete ejecutan las instrucciones una a una y de arriba hacia abajo).
Redeclarar la misma variable dentro del alcance del bloque da SyntaxError.

Pero si es todo lo mismo, contexto léxico, llaves y ámbito de bloque, porque en el primer ejemplo que te puse o este incluso que te pongo a continuación y que NO ESTA ENTRE LLAVES salta el SyntaxError por la redeclaración. Llaves=ámbito de bloque=ámbito léxico. Aquí no hay llaves.

Imaginemos este ejemplo, un script sin llaves:

<script>
const MY_FAV = 7;
var MY_FAV = 20;// // aquín también lanzara un SyntaxError por la redeclaración
</script>
Hola de nuevo, sigo con este tema de ámbito de bloque en JavaScript y acabo de ver un ejemplo que no comprendo.

Con const y let podemos declarar una variable de alcance de bloque({}), hasta aquí todo bien.

Si echamos un vistazo sentencia block de JavaScript(adjunto abajo), veras que como dijimos const y let declara una variable de ámbito de bloque . Pero si vamos al ejemplo final también se puede ver que podemos limitar la declaración de una función dentro del bloque.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/S…
https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Sen…

¿La declaración de una función puede tener un ámbito de bloque? Lo digo, porque lo que crea un ámbito de bloque no es la sentencia block en si ({}) sino, en este caso let y const. ¿Pero, function?

Ejemplo:

nacion('frances'); // TypeError: nacion no es una función
{
function nacion(nacionalidad) {
console.log('Yo soy ' + nacionalidad);
}
nacion('español'); // correcto. logs Yo soy español
}
Lo digo, porque lo que crea un ámbito de bloque no es la sentencia block en si ({}) sino, en este caso let y const

No, lo que crea el nuevo ámbito no son let/const/var (aunque var puede salir del ámbito donde se declaró), sino las llaves ({}):

var a = 'a'; // var puede ser reemplazado, en todos los ámbitos, por let o const.
{ // Ámbito 1
  var a = 'b';
  {  // Ámbito 2
    var a = 'c';
  }
}
alert('A = ' + a);

¿La declaración de una función puede tener un ámbito de bloque?

Pues sí, jajaja. Tiene que ver con varias cosas, pero tal vez la más relevante sea el poder ocultar funciones al resto del mundo. Por ejemplo, el principio abierto/cerrado en programación orientada a objetos dice que:

Un objeto debe estar abierto a extensiones y cerrado a las modificaciones

Esto quiere decir que un objeto (entidad) no debe cambiar su forma de funcionar después de haber sido terminado. Si necesita una nueva característica, entonces se debe crear un nuevo objeto.

Si se intenta seguir lo anterior, en JS, sin funciones de contexto local no podríamos hacerlo, pues no habría forma de limitar el acceso a los usuarios de las funciones que creamos:

function MiCadena() {
  function darVuelta(cadena) {
    let resultado = '';
    for(let i = cadena.length - 1; i >= 0; i--) {
      resultado += cadena[i];
    }
    return resultado;
  }

  return {
    voltear: darVuelta
  };
}
let miCadena = new MiCadena();
alert(miCadena.voltear('Como Crear Tu Web'));

La idea es ocultar lo que hay dentro de darVuelta, así, si modifico el código de darVuelta (puede que hayas encontrado un error, por ejemplo). También se evita que hayan alcances de nombres (que alguien más defina darVuelta en otro script y sobrescriba la mía o genere un error).

Si alguien quisiera añadir una función similar, que volteara las palabras de la cadena en lugar de cada letra, entonces podría hacer esto (en otro script, incluido después del tuyo):

let miCadena2 = new MiCadena();
miCadena2.voltear2 = function(cadena) {
	let split = cadena.split(' ');
  return split.reverse().join(' ');
};

alert(miCadena2.voltear2('Como Crear Tu Web'));

Una aclaración:

El contexto está relacionado al objeto donde se ejecuta o al que pertenece el código del script. Usualmente esto es un objeto (incluso el contexto global o window, que es un objeto).
El ámbito de una variable es el alcance que puede tener este (donde se puede "usar"). Hay dos ámbitos de estas: local o global. Las variables locales son todas aquellas definidas dentro de una función (anónima o no); el resto son globales.

Espero que sirva de algo la explicación :).
pd:

Con let y const

Por el contrario, las variables declaradas con let y const tienen alcance de bloque.

let x = 1;
{
let x = 2;
}
console.log(x); // logs 1

El alcance x = 2 es limitado solamente al bloque en el que está definido.

Si x = 2 no fuera declarado con let sino con var logs no daría este resultado(al margen que sabemos que no se puede redeclarar una variable con let ni const). Aquí se demuestra que no son las llaves las que crean ese ámbito de bloque.
In other words, block statements do not introduce a scope.

Esto es así sólo con var, no con let/const.

Aquí se demuestra que no son las llaves las que crean ese ámbito de bloque

Tal vez estemos hablando de cosas distintas, pero una variable no puede tener declaraciones dentro de ella, por lo tanto tampoco puede crear un contexto o ámbito. Claro, uno puede definir una variable como un objeto donde definir funciones y otras variables, pero en ese caso sería el objeto el que define el contexto y las funciones el ámbito (lo que vaya dentro de los corchetes).

De todas formas, como dice la documentación de MDN:

...you do not want to use standalone blocks in JavaScript, because they don't do what you think they do, if you think they do anything like such blocks in C or Java.
...no querrás usar bloques solitarios en JavaScript, porque no hacen lo crees que hacen, si piensas que hacen algo parecido a los bloques similares en C o Java.

En otros lenguajes tienen sentido, aquí tal vez sirvan para ordenar el código, pero en ese caso sería mejor definir una función.
La declaración de una función por alguna razón tiene un alcance de bloque también como let y const. Pero ni idea del porqué.

Me voy a responder yo mismo por lo que he estado googleando.

Pues simple y llanamente igual que let y const se puede declarar variables de ámbito de bloque (block scope), las declaraciones de las funciones pueden hacer exactamente lo mismo dentro de un bloque y estar limitadas a ese bloque.
Me voy a responder yo mismo por lo que he estado googleando.

La declaración de una función por alguna razón tiene un alcance de bloque también como let y const. Pero ni idea del porqué./

Pues simple y llanamente igual que let y const pueden declarar variables de ámbito de bloque (block scope), las declaraciones de las funciones pueden hacer exactamente lo mismo dentro de un bloque y estar limitadas a ese bloque.
Me voy a responder yo mismo por lo que he estado googleando.

La declaración de una función por alguna razón tiene un alcance de bloque también como let y const. Pero ni idea del porqué.

Pues simple y llanamente igual que let y const pueden declarar variables de ámbito de bloque (block scope), las declaraciones de las funciones pueden hacer exactamente lo mismo dentro de un bloque y estar limitadas a ese bloque.