¿Cuál es el alcance de las variables en javascript? ¿Tienen el mismo alcance dentro que fuera de una función? ó acaso importa? Además, ¿dónde se almacenan las variables si están definidas globalmente?
¿Cuál es el alcance de las variables en javascript? ¿Tienen el mismo alcance dentro que fuera de una función? ó acaso importa? Además, ¿dónde se almacenan las variables si están definidas globalmente?
Respuestas:
JavaScript tiene ámbitos y cierres léxicos (también llamados estáticos). Esto significa que puede determinar el alcance de un identificador mirando el código fuente.
Los cuatro ámbitos son:
Fuera de los casos especiales de alcance global y de módulo, las variables se declaran usando var
(alcance de función), let
(alcance de bloque) yconst
(alcance de bloque). La mayoría de las otras formas de declaración de identificador tienen alcance de bloque en modo estricto.
El alcance es la región de la base de código sobre la cual un identificador es válido.
Un entorno léxico es un mapeo entre los nombres de los identificadores y los valores asociados con ellos.
El alcance está formado por una anidación vinculada de entornos léxicos, con cada nivel en la anidación correspondiente a un entorno léxico de un contexto de ejecución ancestral.
Estos entornos léxicos vinculados forman una "cadena" de alcance. La resolución del identificador es el proceso de búsqueda a lo largo de esta cadena de un identificador coincidente.
La resolución del identificador solo ocurre en una dirección: hacia afuera. De esta manera, los ambientes léxicos externos no pueden "ver" los ambientes léxicos internos.
Hay tres factores pertinentes para decidir el alcance de un identificador en JavaScript:
Algunas de las formas en que se pueden declarar los identificadores:
var
, let
yconst
var
en modo no estricto)import
declaracioneseval
Se pueden declarar algunos de los identificadores de ubicaciones:
Los identificadores declarados usando var
tienen un alcance de función , aparte de cuando se declaran directamente en el contexto global, en cuyo caso se agregan como propiedades en el objeto global y tienen un alcance global. Hay reglas separadas para su uso eneval
funciones.
Identificadores declarados usando let
y const
tienen alcance de bloque , aparte de cuando se declaran directamente en el contexto global, en cuyo caso tienen alcance global.
Nota: let
, const
y var
están todos izada . Esto significa que su posición lógica de definición es la parte superior de su alcance (bloque o función). Sin embargo, las variables declararon su uso let
y const
no pueden leerse ni asignarse hasta que el control haya pasado el punto de declaración en el código fuente. El período intermedio se conoce como la zona muerta temporal.
function f() {
function g() {
console.log(x)
}
let x = 1
g()
}
f() // 1 because x is hoisted even though declared with `let`!
Los nombres de los parámetros de la función están dentro del ámbito de la función. Tenga en cuenta que hay una ligera complejidad en esto. Las funciones declaradas como argumentos predeterminados se cierran sobre la lista de parámetros y no el cuerpo de la función.
Las declaraciones de función tienen alcance de bloque en modo estricto y alcance de función en modo no estricto. Nota: el modo no estricto es un conjunto complicado de reglas emergentes basadas en las extravagantes implementaciones históricas de diferentes navegadores.
Las expresiones de funciones con nombre tienen un alcance propio (p. Ej., Para fines de recursión).
En el modo no estricto, las propiedades definidas implícitamente en el objeto global tienen alcance global, porque el objeto global se encuentra en la parte superior de la cadena de alcance. En modo estricto, estos no están permitidos.
En las eval
cadenas, las variables declaradas usando var
se colocarán en el alcance actual o, si eval
se usa indirectamente, como propiedades en el objeto global.
Lo siguiente arrojará un ReferenceError porque los nombres x
, y
y z
no tienen significado fuera de la función f
.
function f() {
var x = 1
let y = 1
const z = 1
}
console.log(typeof x) // undefined (because var has function scope!)
console.log(typeof y) // undefined (because the body of the function is a block)
console.log(typeof z) // undefined (because the body of the function is a block)
Lo siguiente arrojará un ReferenceError para y
y z
, pero no para x
, porque la visibilidad de x
no está limitada por el bloque. Los bloques que definen los cuerpos de las estructuras de control como if
, for
y while
, se comportan de manera similar.
{
var x = 1
let y = 1
const z = 1
}
console.log(x) // 1
console.log(typeof y) // undefined because `y` has block scope
console.log(typeof z) // undefined because `z` has block scope
A continuación, x
es visible fuera del bucle porque var
tiene alcance de función:
for(var x = 0; x < 5; ++x) {}
console.log(x) // 5 (note this is outside the loop!)
... debido a este comportamiento, debe tener cuidado al cerrar las variables declaradas mediante var
bucles. Solo hay una instancia de variablex
declara , y se ubica lógicamente fuera del bucle.
Las siguientes impresiones 5
, cinco veces, y luego imprime 5
por sexta vez para el console.log
exterior del bucle:
for(var x = 0; x < 5; ++x) {
setTimeout(() => console.log(x)) // closes over the `x` which is logically positioned at the top of the enclosing scope, above the loop
}
console.log(x) // note: visible outside the loop
Lo siguiente se imprime undefined
porque x
tiene un alcance de bloque. Las devoluciones de llamada se ejecutan una por una de forma asincrónica. Nuevo comportamiento de let
los medios de variables que cada función anónima se cerró sobre una variable diferente llamado x
(a diferencia de lo habría hecho con var
), y así los números enteros 0
a través de 4
se imprimen .:
for(let x = 0; x < 5; ++x) {
setTimeout(() => console.log(x)) // `let` declarations are re-declared on a per-iteration basis, so the closures capture different variables
}
console.log(typeof x) // undefined
Lo siguiente NO arrojará un ReferenceError
porque la visibilidad de x
no está limitada por el bloque; sin embargo, se imprimirá undefined
porque la variable no se ha inicializado (debido a la if
declaración).
if(false) {
var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised
Una variable declarada en la parte superior de un for
bucle que se usa let
se limita al cuerpo del bucle:
for(let x = 0; x < 10; ++x) {}
console.log(typeof x) // undefined, because `x` is block-scoped
Lo siguiente arrojará un ReferenceError
porque la visibilidad de x
está limitada por el bloque:
if(false) {
let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped
Las variables declaradas usando var
, let
o const
están en el ámbito de los módulos:
// module1.js
var x = 0
export function f() {}
//module2.js
import f from 'module1.js'
console.log(x) // throws ReferenceError
Lo siguiente declarará una propiedad en el objeto global, porque las variables declaradas usando var
dentro del contexto global, se agregan como propiedades al objeto global:
var x = 1
console.log(window.hasOwnProperty('x')) // true
let
y const
en el contexto global no agregue propiedades al objeto global, pero aún tenga alcance global:
let x = 1
console.log(window.hasOwnProperty('x')) // false
Los parámetros de la función pueden considerarse declarados en el cuerpo de la función:
function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function
Los parámetros del bloque de captura están sujetos al cuerpo del bloque de captura:
try {} catch(e) {}
console.log(typeof e) // undefined, because `e` is scoped to the catch block
Las expresiones de funciones con nombre solo tienen un alcance para la expresión misma:
(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression
En el modo no estricto, las propiedades definidas implícitamente en el objeto global tienen un alcance global. En modo estricto, obtienes un error.
x = 1 // implicitly defined property on the global object (no "var"!)
console.log(x) // 1
console.log(window.hasOwnProperty('x')) // true
En modo no estricto, las declaraciones de función tienen alcance de función. En modo estricto tienen alcance de bloque.
'use strict'
{
function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped
El alcance se define como la región léxica del código sobre la cual un identificador es válido.
En JavaScript, cada objeto de función tiene una [[Environment]]
referencia oculta que es una referencia al entorno léxico del contexto de ejecución. (marco de pila) dentro del cual se creó.
Cuando invocas una función, [[Call]]
se llama al método oculto . Este método crea un nuevo contexto de ejecución y establece un vínculo entre el nuevo contexto de ejecución y el entorno léxico del objeto de función. Lo hace copiando el [[Environment]]
valor en el objeto de función, en un campo de referencia externo en el entorno léxico del nuevo contexto de ejecución.
Tenga en cuenta que este vínculo entre el nuevo contexto de ejecución y el entorno léxico del objeto de función se denomina cierre .
Por lo tanto, en JavaScript, el alcance se implementa a través de entornos léxicos unidos en una "cadena" por referencias externas. Esta cadena de entornos léxicos se denomina cadena de alcance, y la resolución del identificador se produce buscando en la cadena un identificador coincidente.
Averiguar más .
Javascript usa cadenas de alcance para establecer el alcance de una función determinada. Normalmente hay un ámbito global, y cada función definida tiene su propio ámbito anidado. Cualquier función definida dentro de otra función tiene un alcance local que está vinculado a la función externa. Siempre es la posición en la fuente que define el alcance.
Un elemento en la cadena de alcance es básicamente un Mapa con un puntero a su alcance principal.
Al resolver una variable, javascript comienza en el ámbito más interno y busca hacia afuera.
Las variables declaradas globalmente tienen un alcance global. Las variables declaradas dentro de una función tienen un alcance para esa función, y las variables globales de sombra del mismo nombre.
(Estoy seguro de que hay muchas sutilezas que los programadores reales de JavaScript podrán señalar en otras respuestas. En particular, me encontré con esta página sobre lo que this
significa exactamente en cualquier momento. Espero que este enlace más introductorio sea suficiente para comenzar, aunque .)
Tradicionalmente, JavaScript realmente solo tiene dos tipos de alcance:
No voy a dar más detalles sobre esto, ya que hay muchas otras respuestas que explican la diferencia.
Las especificaciones de JavaScript más recientes ahora también permiten un tercer alcance:
Tradicionalmente, creas tus variables así:
var myVariable = "Some text";
Las variables de alcance de bloque se crean así:
let myVariable = "Some text";
Para comprender la diferencia entre el alcance funcional y el alcance de bloque, considere el siguiente código:
// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here
function loop(arr) {
// i IS known here, but undefined
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( var i = 0; i < arr.length; i++ ) {
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( let j = 0; j < arr.length; j++ ) {
// i IS known here, and has a value
// j IS known here, and has a value
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
}
loop([1,2,3,4]);
for( var k = 0; k < arr.length; k++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
};
for( let l = 0; l < arr.length; l++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS known here, and has a value
};
loop([1,2,3,4]);
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
Aquí, podemos ver que nuestra variable j
solo se conoce en el primer bucle for, pero no antes y después. Sin embargo, nuestra variablei
es conocida en toda la función.
Además, tenga en cuenta que las variables de ámbito de bloque no se conocen antes de declararse porque no se izan. Tampoco se le permite volver a declarar la misma variable de ámbito de bloque dentro del mismo bloque. Esto hace que las variables de ámbito de bloque sean menos propensas a errores que las variables de ámbito global o funcional, que se izan y que no producen ningún error en caso de declaraciones múltiples.
Si es seguro usarlo hoy o no, depende de su entorno:
Si está escribiendo código JavaScript del lado del servidor ( Node.js ), puede usar la let
declaración de forma segura .
Si está escribiendo código JavaScript del lado del cliente y usa un transpilador basado en navegador (como Traceur o babel-standalone ), puede usar la let
declaración de forma segura , sin embargo, es probable que su código sea todo menos óptimo con respecto al rendimiento.
Si está escribiendo código JavaScript del lado del cliente y usa un transpilador basado en Nodo (como el script de shell traceur o Babel ), puede usar la let
declaración de manera segura . Y debido a que su navegador solo sabrá sobre el código transpilado, los inconvenientes de rendimiento deben ser limitados.
Si está escribiendo código JavaScript del lado del cliente y no usa un transpilador, debe considerar la compatibilidad con el navegador.
Estos son algunos navegadores que no son compatibles let
en absoluto:
Para obtener una descripción actualizada de qué navegadores admiten la let
declaración al momento de leer esta respuesta, consulte esta Can I Use
página .
(*) Las variables de ámbito global y funcional pueden inicializarse y utilizarse antes de declararse porque las variables de JavaScript se izan . Esto significa que las declaraciones siempre están en lo más alto del alcance.
Aquí hay un ejemplo:
<script>
var globalVariable = 7; //==window.globalVariable
function aGlobal( param ) { //==window.aGlobal();
//param is only accessible in this function
var scopedToFunction = {
//can't be accessed outside of this function
nested : 3 //accessible by: scopedToFunction.nested
};
anotherGlobal = {
//global because there's no `var`
};
}
</script>
Querrás investigar los cierres y cómo usarlos para hacer miembros privados .
La clave, según tengo entendido, es que Javascript tiene un alcance de nivel de función frente al alcance de bloque C más común.
En "Javascript 1.7" (extensión de Mozilla a Javascript) también se pueden declarar variables de alcance de bloque con la let
declaración :
var a = 4;
let (a = 3) {
alert(a); // 3
}
alert(a); // 4
let
.
La idea del alcance en JavaScript cuando fue diseñada originalmente por Brendan Eich surgió del lenguaje de script HyperCard HyperTalk .
En este idioma, las pantallas se hicieron de forma similar a una pila de fichas. Había una tarjeta maestra conocida como fondo. Era transparente y puede verse como la carta inferior. Cualquier contenido en esta tarjeta base se compartió con tarjetas colocadas encima de ella. Cada tarjeta colocada en la parte superior tenía su propio contenido que tenía prioridad sobre la tarjeta anterior, pero aún tenía acceso a las tarjetas anteriores si lo deseaba.
Así es exactamente cómo está diseñado el sistema de alcance de JavaScript. Solo tiene diferentes nombres. Las tarjetas en JavaScript se conocen como contextos de ejecución ECMA . Cada uno de estos contextos contiene tres partes principales. Un entorno variable, un entorno léxico y este enlace. Volviendo a la referencia de las cartas, el entorno léxico contiene todo el contenido de las cartas anteriores más abajo en la pila. El contexto actual está en la parte superior de la pila y cualquier contenido declarado allí se almacenará en el entorno variable. El entorno variable tendrá prioridad en el caso de colisiones de nombres.
El enlace this apuntará al objeto contenedor. A veces, los ámbitos o los contextos de ejecución cambian sin que cambie el objeto contenedor, como en una función declarada donde puede estar el objeto contenedor window
o una función constructora.
Estos contextos de ejecución se crean cada vez que se transfiere el control. El control se transfiere cuando el código comienza a ejecutarse, y esto se hace principalmente desde la ejecución de la función.
Esa es la explicación técnica. En la práctica, es importante recordar que en JavaScript
Aplicando esto a uno de los ejemplos anteriores (5. "Cierre") en esta página, es posible seguir la pila de contextos de ejecución. En este ejemplo, hay tres contextos en la pila. Están definidos por el contexto externo, el contexto en la función invocada inmediatamente llamada por var six, y el contexto en la función devuelta dentro de la función invocada inmediatamente por var six.
i ) El contexto externo. Tiene un entorno variable de a = 1
ii ) El contexto IIFE, tiene un entorno léxico de a = 1, pero un entorno variable de a = 6 que tiene prioridad en la pila
iii ) El contexto de la función devuelta, tiene un léxico entorno de a = 6 y ese es el valor referenciado en la alerta cuando se llama.
1) Existe un ámbito global, un ámbito de función y los ámbitos con y catch. No existe un alcance de nivel de 'bloque' en general para las variables: las declaraciones with y catch agregan nombres a sus bloques.
2) Los ámbitos están anidados por funciones hasta el alcance global.
3) Las propiedades se resuelven pasando por la cadena del prototipo. La instrucción with lleva los nombres de las propiedades del objeto al ámbito léxico definido por el bloque with.
EDITAR: ECMAAScript 6 (Armonía) está especificado para admitir let, y sé que Chrome permite una bandera de "armonía", por lo que tal vez sí lo admite.
Let sería un soporte para el alcance del nivel de bloque, pero debe usar la palabra clave para que suceda.
EDITAR: Basándome en que Benjamin señaló las declaraciones con y en los comentarios, edité la publicación y agregué más. Tanto las declaraciones con como las capturas introducen variables en sus bloques respectivos, y ese es un alcance de bloque. Estas variables tienen un alias a las propiedades de los objetos que se les pasan.
//chrome (v8)
var a = { 'test1':'test1val' }
test1 // error not defined
with (a) { var test1 = 'replaced' }
test1 // undefined
a // a.test1 = 'replaced'
EDITAR: Ejemplo de aclaración:
test1 tiene un alcance con el bloque with, pero tiene un alias de a.test1. 'Var test1' crea una nueva variable test1 en el contexto léxico superior (función o global), a menos que sea una propiedad de a, que lo es.
¡Ay! Tenga cuidado al usar 'con', al igual que var es un noop si la variable ya está definida en la función, ¡también es un noop con respecto a los nombres importados del objeto! Un pequeño aviso sobre el nombre que ya está definido haría esto mucho más seguro. Personalmente nunca lo usaré debido a esto.
with
declaración es una forma de alcance de bloque, pero las catch
cláusulas son una forma mucho más común (Dato curioso, v8 se implementa catch
con a with
): esas son prácticamente las únicas formas de alcance de bloque en JavaScript (es decir, función, global, try / catch , con y sus derivados), sin embargo, los entornos host tienen diferentes nociones de alcance, por ejemplo, eventos en línea en el navegador y el módulo vm de NodeJS.
Descubrí que muchas personas nuevas en JavaScript tienen problemas para comprender que la herencia está disponible por defecto en el lenguaje y que el alcance de la función es el único alcance, hasta ahora. Proporcioné una extensión a un embellecedor que escribí a fines del año pasado llamado JSPretty. Los colores de función funcionan en el alcance del código y siempre asocian un color a todas las variables declaradas en ese alcance. El cierre se demuestra visualmente cuando una variable con un color de un alcance se usa en un alcance diferente.
Pruebe la función en:
Vea una demostración en:
Ver el código en:
Actualmente, la función ofrece soporte para una profundidad de 16 funciones anidadas, pero actualmente no colorea variables globales.
JavaScript tiene solo dos tipos de alcance:
var
palabra clave tiene alcance funcional.Cada vez que se llama a una función, se crea un objeto de alcance variable (y se incluye en la cadena de alcance) seguido de variables en JavaScript.
a = "global";
function outer(){
b = "local";
console.log(a+b); //"globallocal"
}
outer();
Cadena de alcance ->
a
y outer
función están en el nivel superior en cadena de ámbitos.variable scope object
(e incluida en la cadena de alcance) agregada con una variable b
dentro de ella.Ahora, cuando se a
requiere una variable , primero busca el alcance de la variable más cercana y, si la variable no está allí, se mueve al siguiente objeto de la cadena de alcance de la variable, que en este caso es el nivel de la ventana.
Solo para agregar a las otras respuestas, el alcance es una lista de búsqueda de todos los identificadores (variables) declarados, y aplica un conjunto estricto de reglas sobre cómo son accesibles para el código que se está ejecutando actualmente. Esta búsqueda puede ser con el propósito de asignar a la variable, que es una referencia LHS (lado izquierdo), o puede ser con el propósito de recuperar su valor, que es una referencia RHS (lado derecho). Estas búsquedas son lo que el motor de JavaScript hace internamente cuando compila y ejecuta el código.
Desde esta perspectiva, creo que una imagen ayudaría que encontré en el libro electrónico Scopes and Closures de Kyle Simpson:
Citando de su libro electrónico:
El edificio representa el conjunto de reglas de alcance anidado de nuestro programa. El primer piso del edificio representa su alcance actual en ejecución, esté donde esté. El nivel superior del edificio es el alcance global. Usted resuelve las referencias de LHS y RHS mirando su piso actual, y si no lo encuentra, tome el elevador al siguiente piso, mire allí, luego al siguiente, y así sucesivamente. Una vez que llegue al piso superior (el alcance global), encontrará lo que está buscando o no. Pero tienes que parar de todos modos.
Una cosa de nota que vale la pena mencionar, "La búsqueda del alcance se detiene una vez que encuentra la primera coincidencia".
Esta idea de "niveles de alcance" explica por qué "esto" se puede cambiar con un alcance recién creado, si se busca en una función anidada. Aquí hay un enlace que incluye todos estos detalles. Todo lo que quería saber sobre el alcance de JavaScript.
ejecuta el código. espero que esto dé una idea sobre el alcance
Name = 'global data';
document.Name = 'current document data';
(function(window,document){
var Name = 'local data';
var myObj = {
Name: 'object data',
f: function(){
alert(this.Name);
}
};
myObj.newFun = function(){
alert(this.Name);
}
function testFun(){
alert("Window Scope : " + window.Name +
"\nLocal Scope : " + Name +
"\nObject Scope : " + this.Name +
"\nCurrent document Scope : " + document.Name
);
}
testFun.call(myObj);
})(window,document);
Las variables globales son exactamente como las estrellas globales (Jackie Chan, Nelson Mandela). Puede acceder a ellos (obtener o establecer el valor), desde cualquier parte de su aplicación. Las funciones globales son como eventos globales (Año Nuevo, Navidad). Puede ejecutarlos (llamarlos) desde cualquier parte de su aplicación.
//global variable
var a = 2;
//global function
function b(){
console.log(a); //access global variable
}
Si estás en los EE. UU., Es posible que conozcas a Kim Kardashian, famosa celebridad (de alguna manera se las arregla para hacer los tabloides). Pero las personas fuera de los Estados Unidos no la reconocerán. Ella es una estrella local, ligada a su territorio.
Las variables locales son como estrellas locales. Solo puede acceder a ellos (obtener o establecer el valor) dentro del alcance. Una función local es como eventos locales: solo puede ejecutar (celebrar) dentro de ese ámbito. Si desea acceder a ellos desde fuera del alcance, obtendrá un error de referencia
function b(){
var d = 21; //local variable
console.log(d);
function dog(){ console.log(a); }
dog(); //execute local function
}
console.log(d); //ReferenceError: dddddd is not defined
Consulte este artículo para conocer en profundidad el alcance
Casi solo hay dos tipos de ámbitos de JavaScript:
Por lo tanto, los bloques que no sean funciones no crean un nuevo alcance. Eso explica por qué los bucles for sobrescriben las variables de ámbito externo:
var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// output 5 5
Usando funciones en su lugar:
var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i,v);
// output 10 10
En el primer ejemplo, no había un alcance de bloque, por lo que las variables inicialmente declaradas se sobrescribieron. En el segundo ejemplo, había un nuevo alcance debido a la función, por lo que las variables inicialmente declaradas se SOMBRARON y no se sobrescribieron.
Eso es casi todo lo que necesita saber en términos de alcance de JavaScript, excepto:
Por lo tanto, puede ver que el alcance de JavaScript es en realidad extremadamente simple, aunque no siempre intuitivo. Algunas cosas a tener en cuenta:
Entonces este código:
var i = 1;
function abc() {
i = 2;
var i = 3;
}
console.log(i); // outputs 1
es equivalente a:
var i = 1;
function abc() {
var i; // var declaration moved to the top of the scope
i = 2;
i = 3; // the assignment stays where it is
}
console.log(i);
Esto puede parecer contrario a la intuición, pero tiene sentido desde la perspectiva de un diseñador de lenguaje imperativo.
const
' y ' let
'Debería usar el alcance de bloque para cada variable que cree, al igual que la mayoría de los otros idiomas principales. var
Es obsoleto . Esto hace que su código sea más seguro y más fácil de mantener.
const
debe usarse para el 95% de los casos . Hace que la referencia variable no pueda cambiar. Las propiedades de matriz, objeto y nodo DOM pueden cambiar y probablemente deberían serlo const
.
let
debe usarse para cualquier variable que espera ser reasignada. Esto incluye dentro de un bucle for. Si alguna vez cambia el valor más allá de la inicialización, uselet
.
El alcance del bloque significa que la variable solo estará disponible entre los corchetes en los que se declara. Esto se extiende a los ámbitos internos, incluidas las funciones anónimas creadas dentro de su ámbito.
Prueba este curioso ejemplo. En el ejemplo a continuación, si a fuera un valor numérico inicializado en 0, vería 0 y luego 1. Excepto que a es un objeto y javascript le pasará a f1 un puntero de en lugar de una copia del mismo. El resultado es que obtienes la misma alerta las dos veces.
var a = new Date();
function f1(b)
{
b.setDate(b.getDate()+1);
alert(b.getDate());
}
f1(a);
alert(a.getDate());
Solo hay ámbitos de función en JS. ¡No bloquear ámbitos! También puedes ver lo que está levantando.
var global_variable = "global_variable";
var hoisting_variable = "global_hoist";
// Global variables printed
console.log("global_scope: - global_variable: " + global_variable);
console.log("global_scope: - hoisting_variable: " + hoisting_variable);
if (true) {
// The variable block will be global, on true condition.
var block = "block";
}
console.log("global_scope: - block: " + block);
function local_function() {
var local_variable = "local_variable";
console.log("local_scope: - local_variable: " + local_variable);
console.log("local_scope: - global_variable: " + global_variable);
console.log("local_scope: - block: " + block);
// The hoisting_variable is undefined at the moment.
console.log("local_scope: - hoisting_variable: " + hoisting_variable);
var hoisting_variable = "local_hoist";
// The hoisting_variable is now set as a local one.
console.log("local_scope: - hoisting_variable: " + hoisting_variable);
}
local_function();
// No variable in a separate function is visible into the global scope.
console.log("global_scope: - local_variable: " + local_variable);
Tengo entendido que hay 3 ámbitos: alcance global, disponible a nivel mundial; alcance local, disponible para una función completa independientemente de los bloques; y alcance del bloque, solo disponible para el bloque, la declaración o la expresión en la que se utilizó. El alcance global y local se indica con la palabra clave 'var', ya sea dentro de una función o fuera, y el alcance del bloque se indica con la palabra clave 'let'.
Para aquellos que creen que solo hay un alcance global y local, explique por qué Mozilla tendría una página completa que describe los matices del alcance del bloque en JS.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
Un problema muy común que aún no se describe y que los codificadores de front-end suelen encontrar es el alcance que es visible para un controlador de eventos en línea en el HTML, por ejemplo, con
<button onclick="foo()"></button>
El alcance de las variables a las que on*
puede hacer referencia un atributo debe ser:
querySelector
como señalará una variable independiente document.querySelector
; raro)De lo contrario, obtendrá un ReferenceError cuando se invoque el controlador. Entonces, por ejemplo, si el controlador en línea hace referencia a una función que está definida en el interior window.onload
o $(function() {
, la referencia fallará, porque el controlador en línea solo puede hacer referencia a variables en el ámbito global, y la función no es global:
Propiedades de la document
y propiedades del elemento el controlador está unido a también puede ser referenciado como variables independientes dentro de los manipuladores en línea porque los manipuladores de línea se invocan dentro de dos with
bloques , uno para el document
, uno para el elemento. La cadena de variables de alcance dentro de estos manejadores es extremadamente poco intuitiva , y un manejador de eventos de trabajo probablemente requerirá que una función sea global (y probablemente se debe evitar la contaminación global innecesaria ).
Dado que la cadena de alcance dentro de los controladores en línea es muy extraña , y dado que los controladores en línea requieren contaminación global para funcionar, y dado que los controladores en línea a veces requieren un escape de cadena feo al pasar argumentos, probablemente sea más fácil evitarlos. En su lugar, adjunte controladores de eventos usando Javascript (como con addEventListener
), en lugar de con marcado HTML.
En una nota diferente, a diferencia de las <script>
etiquetas normales , que se ejecutan en el nivel superior, el código dentro de los módulos ES6 se ejecuta en su propio ámbito privado. Una variable definida en la parte superior de una <script>
etiqueta normal es global, por lo que puede hacer referencia a ella en otras <script>
etiquetas, como esta:
Pero el nivel superior de un módulo ES6 no es global. Una variable declarada en la parte superior de un módulo ES6 solo será visible dentro de ese módulo, a menos que la variable se edite explícitamente export
o que se asigne a una propiedad del objeto global.
El nivel superior de un módulo ES6 es similar al del interior de un IIFE en el nivel superior de forma normal <script>
. El módulo puede hacer referencia a cualquier variable que sea global, y nada puede hacer referencia a nada dentro del módulo a menos que el módulo esté específicamente diseñado para él.
En JavaScript hay dos tipos de alcance:
La siguiente función tiene una variable de alcance local carName
. Y esta variable no es accesible desde fuera de la función.
function myFunction() {
var carName = "Volvo";
alert(carName);
// code here can use carName
}
La clase de abajo tiene una variable de alcance global carName
. Y esta variable es accesible desde cualquier lugar de la clase.
class {
var carName = " Volvo";
// code here can use carName
function myFunction() {
alert(carName);
// code here can use carName
}
}
ES5
y anterior:Las variables en Javascript fueron inicialmente (pre ES6
) léxicamente alcanzadas. El término de ámbito léxico significa que puede ver el alcance de las variables 'mirando' el código.
Cada variable declarada con la var
palabra clave tiene un alcance de la función. Sin embargo, si se declaran otras funciones dentro de esa función, esas funciones tendrán acceso a las variables de las funciones externas. Esto se llama una cadena de alcance . Funciona de la siguiente manera:
// global scope
var foo = 'global';
var bar = 'global';
var foobar = 'global';
function outerFunc () {
// outerFunc scope
var foo = 'outerFunc';
var foobar = 'outerFunc';
innerFunc();
function innerFunc(){
// innerFunc scope
var foo = 'innerFunc';
console.log(foo);
console.log(bar);
console.log(foobar);
}
}
outerFunc();
¿Qué pasa cuando estamos tratando de registrar las variables foo
, bar
y foobar
que la consola es la siguiente:
innerFunc
propia función . Por lo tanto, el valor de foo se resuelve en la cadena innerFunc
.innerFunc
misma. Por lo tanto, necesitamos escalar la cadena de alcance . Primero observamos la función externa en la que innerFunc
se definió la función . Esta es la función outerFunc
. En el alcance de outerFunc
podemos encontrar la barra variable, que contiene la cadena 'outsideFunc'.ES6
(ES 2015) y mayores:Los mismos conceptos de ámbito léxico y ámbito de aplicación todavía se aplican ES6
. Sin embargo, se introdujeron nuevas formas de declarar variables. Existen los siguientes:
let
: crea una variable de ámbito de bloqueconst
: crea una variable de ámbito de bloque que debe inicializarse y no puede reasignarseLa mayor diferencia entre var
y let
/ const
es que var
tiene un alcance de función mientras que let
/ const
tiene un alcance de bloque. Aquí hay un ejemplo para ilustrar esto:
let letVar = 'global';
var varVar = 'global';
function foo () {
if (true) {
// this variable declared with let is scoped to the if block, block scoped
let letVar = 5;
// this variable declared with let is scoped to the function block, function scoped
var varVar = 10;
}
console.log(letVar);
console.log(varVar);
}
foo();
En el ejemplo anterior, letVar registra el valor global porque las variables declaradas con let
ámbito de bloque. Dejan de existir fuera de su bloque respectivo, por lo que no se puede acceder a la variable fuera del bloque if.
En EcmaScript5, existen principalmente dos ámbitos, ámbito local y ámbito global, pero en EcmaScript6 tenemos principalmente tres ámbitos, ámbito local, ámbito global y un nuevo ámbito denominado ámbito de bloque .
Ejemplo de alcance de bloque es: -
for ( let i = 0; i < 10; i++)
{
statement1...
statement2...// inside this scope we can access the value of i, if we want to access the value of i outside for loop it will give undefined.
}
ECMAScript 6 introdujo las palabras clave let y const. Estas palabras clave se pueden usar en lugar de la palabra clave var. Contrariamente a la palabra clave var, las palabras clave let y const admiten la declaración de alcance local dentro de las declaraciones de bloque.
var x = 10
let y = 10
const z = 10
{
x = 20
let y = 20
const z = 20
{
x = 30
// x is in the global scope because of the 'var' keyword
let y = 30
// y is in the local scope because of the 'let' keyword
const z = 30
// z is in the local scope because of the 'const' keyword
console.log(x) // 30
console.log(y) // 30
console.log(z) // 30
}
console.log(x) // 30
console.log(y) // 20
console.log(z) // 20
}
console.log(x) // 30
console.log(y) // 10
console.log(z) // 10
Realmente me gusta la respuesta aceptada pero quiero agregar esto:
Scope recopila y mantiene una lista de búsqueda de todos los identificadores (variables) declarados, y aplica un conjunto estricto de reglas sobre cómo son accesibles para el código que se está ejecutando actualmente.
El alcance es un conjunto de reglas para buscar variables por su nombre de identificador.
Hay dos tipos de ámbitos en JavaScript.
Alcance global : la variable que se anuncia en el alcance global se puede usar en cualquier parte del programa sin problemas. Por ejemplo:
var carName = " BMW";
// code here can use carName
function myFunction() {
// code here can use carName
}
Ámbito funcional o ámbito local : la variable declarada en este ámbito solo se puede utilizar en su propia función. Por ejemplo:
// code here can not use carName
function myFunction() {
var carName = "BMW";
// code here can use carName
}