Entendiendo el scope en JavaScript
- ¿Que es el Scope?
- Tipos de scope
- Tipos de declaración de variable
- Hoisting
- Uso del navegador para ver el scope
- Conclusiones
¿Qué es el Scope?
En JavaScript, el término scope (o ámbito) se refiere al contexto en el cual se definen y pueden accederse las variables, funciones y objetos en tu código. Este contexto determina la visibilidad y vida útil de las variables y otras declaraciones.
Tipos de scope
JavaScript cuenta con los siguientes tipos de scope:
Global scope
El scope global en JavaScript se refiere al contexto más amplio dentro del cual las variables y funciones están disponibles y accesibles en todo el programa. Las variables definidas en el scope global se pueden usar en cualquier parte del código, ya sea dentro de funciones, bloques u otros contextos.
Module scope
Se trata del scope para el código ejecutándose en un módulo. El scope de módulo en JavaScript es un concepto introducido con los módulos de ECMAScript 6 (ES6), los cuales permiten organizar y dividir el código en archivos y módulos reutilizables.
¿Qué es un módulo? Se trata de un fragmento de código auto-contenido que se puede exportar e importar entre diferentes partes de una aplicación, facilitando la organización y reutilización del código. Los módulos permiten dividir el código en partes más pequeñas y manejables, cada una con su propio scope, ayudando a mantener un código más limpio y modular.
Cada módulo tiene su propio scope, lo que significa que las variables, funciones y clases definidas en un módulo no son accesibles fuera de él a menos que sean explícitamente exportadas. Cuenta con ciertas ventajas, como una mejor organización del código, un encapsulamiento que nos permite prevenir conflictos, una sencilla reutilización del código en diferentes partes de nuestra aplicación y hace que nuestro código sea más fácil de mantener.
Function scope
El scope de función en JavaScript se refiere al contexto creado por una función. Las variables y funciones definidas dentro de este contexto son locales a la función y no se puede acceder a las mismas desde fuera de la función.
Block scope
El scope de bloque en JavaScript se refiere al contexto creado por cualquier par de llaves {}
, como las que se usan en declaraciones if
, bucles for
, while
, y bloques de código. A diferencia del scope de función, que es limitado a la definición de la función, el scope de bloque permite declarar variables que solo son accesibles dentro de un bloque específico de código.
Tipos de declaración de variable
Uno de los puntos más importantes que tenemos que conocer a la hora de hablar del scope son los tipos de declaración de variable que existen, ya que las variables que declaremos van a contar con un comportamiento diferente en base a como las declaremos.
- let: Las declaraciones de tipo
let
presentan un scope de bloque, la cual, opcionalmente, puede ser inicializada con algún valor. - const: Las declaraciones de tipo
const
presentan un scope de bloque tal y como lo hacen las variables definidas usando la instrucciónlet
, con la particularidad de que el valor de una constante no puede cambiarse a través de la reasignación. Las constantes no se pueden redeclarar. - var: Las declaraciones de tipo
var
presentan un scope de función. El scope de una variable declarada con la palabra reservadavar
es su contexto de ejecución en curso, que puede ser la función que la contiene o, para las variables declaradas afuera de cualquier función, un ámbito global.
Recomendaciones acerca de la declaración de variables
Hasta la llegada de ES6, el único tipo de declaración de variable con la que contábamos era var
, este tipo de declaración, debido a su comportamiento y como funciona, podía provocar una gran cantidad de situaciones extrañas en nuestro código y diferentes tipos de bugs. Por ello era necesaria la aparición de estos dos nuevos tipos de declaración de variables,let
y const
, que contaban con un comportamiento mucho más controlado, evitando así todos los problemas que ocasionaba el uso de var
. Por lo que a día de hoy, a pesar de que está la posibilidad de utilizarla, no se recomienda para nada el uso de la misma, y se recomienda declarar las variables haciendo uso de const
siempre que se pueda, y en caso de que sea necesario hacer uso de let
.
Hoisting
Concepto de Hoisting
Otro concepto que es interesante que conozcamos cuando hablamos de scope es el Hoisting. Se trata de un comportamiento del intérprete de JavaScript que mueve las declaraciones de variables y funciones al comienzo de su scope antes de que cualquier código sea ejecutado. Durante la fase de compilación del código, las declaraciones de variables y funciones se asignan en memoria, elevando la declaración al nivel más alto del scope en el que se encuentran.
Detalles acerca del Hoisting
Hay ciertos aspectos a considerar sobre el hoisting. Primero, se guarda en memoria la declaración de variables y funciones, no sus asignaciones. Segundo, en el caso de variables, esto solo ocurre cuando se declaran con var
. Si se usan let
o const
, entran en la llamada temporal dead zone, de la que hablaremos más adelante.
Ejemplos de Hoisting
Para entender cómo funciona el hoisting, vamos a ver algunos ejemplos. Usaremos RunJS, una aplicación que permite ejecutar JavaScript sin configuración previa. En los ejemplos, el código está a la izquierda y la salida a la derecha.
Hoisting con diferentes tipos de variables
En esta imagen, se hace uso de una variable hoistedVar
antes de su declaración, y como podemos apreciar, está imprimiendo el valor undefined
en vez de un mensaje de error indicando que la variable no existe o que no ha sido inicializada. ¿Por qué ocurre esto? JavaScript está elevando la declaración de la variable al principio del scope y está transpilando el código de la siguiente manera.
Elevando la declaración de la variable y una línea después de la impresión de pantalla le está asignando un valor, por eso ha pintado undefined
en la respuesta, ya que aún no le ha asignado un valor.
Intentar lo mismo con let
, const
o class
no funcionará. Al intentar acceder al valor de la variable obtenemos un error indicando que no está definida. Esto ocurre por que las variables mencionadas anteriormente se encuentran en algo llamado temporal dead zone hasta que son declaradas e inicializadas. Cualquier intento de acceso a las mismas antes de que se inicialicen provocará un ReferenceError, como hemos visto en la imagen anterior.
Hoisting de funciones
Ahora, veremos un ejemplo del hoisting con funciones.
La función en este caso se encarga de imprimir por pantalla un mensaje, la cosa es que hemos hecho la llamada de esta función antes de declararla, debido al hoisting, esa declaración de la función junto con la implementación de la misma se guardará en memoria. Pudiendo ac proceso de compilación de JavaScript y podrá accederse a la misma antes como se puede ver en la imagen anterior. En el caso de que guardáramos esta función dentro de una variable, ocurriría lo mismo que en el ejemplo anterior, se elevaría la declaración, pero no la asignación.
Scope chain
Otro concepto que es interesante conocer es el del Scope chain, gracias a este vamos a entender que acciones realiza JavaScript a la hora de buscar variables y trabajar con variables globales, tenemos este ejemplo de código:
Se trata de un código bastante simple, que va a imprimir dos variables. Lo importante aquí es que entendamos el proceso que sigue JavaScript para encontrar las variables. Dentro de la función myFnInside
, se intenta imprimir el valor de x
, que está en un contexto global, fuera de la función myFn
. ¿Que va a hacer JavaScript para encontrar el valor de esa variable?
JavaScript va a ir buscando la variable buscando bloque por bloque, en la imagen anterior se ha indicado por números el orden que seguiría, comenzaría primero buscando la variable dentro del scope de su bloque, al no encontrarlo hará la búsqueda en el siguiente scope que se encuentra nivel superior, hasta encontrarlo.
En el caso de que modificáramos el valor de la variable, el valor que se imprimiría es el que haya encontrado antes dentro del recorrido que hace en cada uno de los bloques.
Aquí no llega a acceder al valor de x
que hemos asignado al principio del código, ha llegado antes al valor inicializado en una variable con el mismo nombre dentro de la función, esto que ha ocurrido se le conoce como variable shadowing, y vendría a ser que el valor de una variable con un nombre en un scope superior ha sido ocultada por una variable con el mismo nombre localizada con un scope más reducido.
Si intentamos acceder desde un scope en un nivel más alto, vemos que el valor de la variable x
inicial no ha sido ocultado, ya que no ha accedido a el valor modificado en el scope dentro del bloque de declaración de la función myFn
.
Uso del navegador para ver el scope
La gran mayoría de navegadores modernos cuentan con herramientas de desarrollador que nos permiten visualizar los diferentes scopes que tenemos en nuestras aplicaciones y las variables que tienen contenidos cada uno.
Para poder verlo solo necesitaríamos abrir las herramientas de desarrollador de nuestro navegador en el sitio web del que queramos ver al información e iremos a la pestaña de sources de nuestras herramientas de desarrollador.
Posteriormente, pondremos un punto de ruptura en el punto donde queremos que se para la ejecución del código para empezar a hacer debug, podremos visualizar un apartado en el que se expondrán los diferentes scopes de nuestra aplicación como se muestra en la siguiente imagen.
Vemos que la variable z está dentro del scope local, este vendría a ser el scope dentro de la función myFnInside
. Si pruebo a poner otro punto de ruptura dentro del bloque del if
nos encontraremos lo siguiente:
Veremos que aparece un nuevo tipo de scope, el de Block, que vendría a ser el scope que se ha generado dentro del bloque del if
, formado gracias al uso de las llaves {}
. Si vamos un poco más allá veremos que a nivel de script general, tenemos el valor de x.
Conclusiones
En este artículo hemos visto como funciona el scope en JavaScript, explicando diferentes tipos de scope que existen, tipos de variables, hoisting, ciertas situaciones que se pueden dar y como utilizar herramientas de desarrollador para ver de una forma más visual los diferentes scopes de una aplicación. El scope es un elemento de JavaScript que forma de las bases del mismo y que es muy importante entender el como funciona. El entender el funcionamiento del mismo nos permite entender mucho mejor como funciona por dentro nuestras aplicaciones web.