Pregunta ¿Cuál es el alcance de las variables en JavaScript?


¿Cuál es el alcance de las variables en javascript? ¿Tienen el mismo alcance dentro que en el exterior de una función? ó acaso importa? Además, ¿dónde están almacenadas las variables si están definidas globalmente?


1711
2018-02-01 08:27


origen


Respuestas:


Creo que lo mejor que puedo hacer es darte un montón de ejemplos para estudiar. Los programadores de Javascript están prácticamente clasificados según qué tan bien entienden el alcance. A veces puede ser bastante contrario a la intuición.

  1. Una variable de ámbito global

    // global scope
    var a = 1;
    
    function one() {
      alert(a); // alerts '1'
    }
    
  2. Alcance local

    // global scope
    var a = 1;
    
    function two(a) {
      // local scope
      alert(a); // alerts the given argument, not the global value of '1'
    }
    
    // local scope again
    function three() {
      var a = 3;
      alert(a); // alerts '3'
    }
    
  3. Intermedio: No hay tal cosa como el alcance del bloque en JavaScript (ES5; ES6 presenta let)

    a.

    var a = 1;
    
    function four() {
      if (true) {
        var a = 4;
      }
    
      alert(a); // alerts '4', not the global value of '1'
    }
    

    segundo.

    var a = 1;
    
    function one() {
      if (true) {
        let a = 4;
      }
    
      alert(a); // alerts '1' because the 'let' keyword uses block scoping
    }
    
  4. Intermedio: Propiedades del objeto

    var a = 1;
    
    function Five() {
      this.a = 5;
    }
    
    alert(new Five().a); // alerts '5'
    
  5. Avanzado: Cierre

    var a = 1;
    
    var six = (function() {
      var a = 6;
    
      return function() {
        // JavaScript "closure" means I have access to 'a' in here,
        // because it is defined in the function in which I was defined.
        alert(a); // alerts '6'
      };
    })();
    
  6. Avanzado: Resolución de alcance basada en prototipos

    var a = 1;
    
    function seven() {
      this.a = 7;
    }
    
    // [object].prototype.property loses to
    // [object].property in the lookup chain. For example...
    
    // Won't get reached, because 'a' is set in the constructor above.
    seven.prototype.a = -1;
    
    // Will get reached, even though 'b' is NOT set in the constructor.
    seven.prototype.b = 8;
    
    alert(new seven().a); // alerts '7'
    alert(new seven().b); // alerts '8'
    

  7. Global + Local: Un caso extra complejo

    var x = 5;
    
    (function () {
        console.log(x);
        var x = 10;
        console.log(x); 
    })();
    

    Esto se imprimirá undefined y 10 más bien que 5 y 10 ya que JavaScript siempre mueve las declaraciones de variables (no las inicializaciones) al principio del alcance, haciendo que el código sea equivalente a:

    var x = 5;
    
    (function () {
        var x;
        console.log(x);
        x = 10;
        console.log(x); 
    })();
    
  8. Variable de Catch Clause-Range

    var e = 5;
    console.log(e);
    try {
        throw 6;
    } catch (e) {
        console.log(e);
    }
    console.log(e);
    

    Esto se imprimirá 5, 6, 5. Dentro de la cláusula de captura e sombrea las variables globales y locales. Pero este alcance especial es solo para la variable capturada. Si tú escribes var f; dentro de la cláusula catch, entonces es exactamente lo mismo que si lo hubieras definido antes o después del bloque try-catch.


2262
2018-02-01 08:58



Javascript usa cadenas de alcance para establecer el alcance de una función determinada. Normalmente hay un alcance global, y cada función definida tiene su propio alcance 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.


219
2018-02-01 08:35



Las variables declaradas globalmente tienen un alcance global. Las variables declaradas dentro de una función tienen un alcance para esa función y oscilan las variables globales del mismo nombre.

(Estoy seguro de que hay muchas sutilezas que los programadores de JavaScript reales podrán señalar en otras respuestas. En particular, me encontré con esta página sobre qué exactamente this significa en cualquier momento. Ojalá este enlace más introductorio es suficiente para que comiences sin embargo).


93
2018-02-01 08:31



Old School JavaScript

Tradicionalmente, JavaScript realmente solo tiene dos tipos de alcance:

  1. Alcance global : Las variables son conocidas en toda la aplicación, desde el inicio de la aplicación (*)
  2. Alcance funcional : Las variables son conocidas dentro de la función se declaran en, desde el inicio de la función (*)

No daré más detalles sobre esto, ya que ya hay muchas otras respuestas que explican la diferencia.


JavaScript moderno

los las especificaciones de JavaScript más recientes ahora también permite un tercer alcance:

  1. Alcance del bloque : Las variables son conocidas dentro de el bloquese declaran desde el momento en que se declaran en adelante (**)

¿Cómo creo variables de ámbito de bloque?

Tradicionalmente, creas tus variables de esta manera:

var myVariable = "Some text";

Las variables de ámbito de bloque se crean así:

let myVariable = "Some text";

Entonces, ¿cuál es la diferencia entre el alcance funcional y el alcance del bloque?

Para entender la diferencia entre el alcance funcional y el alcance del 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 ciclo for, pero no antes y después. Sin embargo, nuestra variable i es conocido en toda la función.

Además, considere que las variables de ámbito de bloque no se conocen antes de que se declaren porque no están elevadas. Tampoco puede redeclarar 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 generan y que no producen ningún error en el caso de declaraciones múltiples.


¿Es seguro usar variables de alcance de bloque hoy?

Si es seguro usar hoy o no, depende de su entorno:

  • Si está escribiendo código JavaScript del lado del servidor (Node.js), puedes usar el let declaración.

  • Si está escribiendo un código JavaScript del lado del cliente y usa un transpiler (como Traceur), puedes usar el let declaración, sin embargo, es probable que su código sea cualquier cosa menos óptimo con respecto al rendimiento.

  • Si está escribiendo un código JavaScript del lado del cliente y no usa un transpiler, debe considerar la compatibilidad con el navegador.

    Hoy, 23 de febrero de 2016, estos son algunos navegadores que no son compatibles let o solo tiene soporte parcial:

    • Internet Explorer 10 y abajo (sin soporte)
    • Firefox 43 y abajo (sin soporte)
    • Safari 9 y abajo (sin soporte)
    • Opera Mini 8 y abajo (sin soporte)
    • Navegador Android 4 y abajo (sin soporte)
    • Opera 36 y abajo (soporte parcial)
    • Chome 51 y abajo (soporte parcial)

enter image description here


Cómo realizar un seguimiento del soporte del navegador

Para obtener una descripción actualizada de qué navegadores admiten la let declaración al momento de leer esta respuesta, ver esta Can I Use página.


(*) Las variables con ámbito global y funcional se pueden inicializar y usar antes de que se declaren porque las variables de JavaScript son izada. Esto significa que las declaraciones siempre son muy importantes.

(**) Las variables de ámbito de bloque no se izan


55
2018-02-23 18:51



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á investigar cierres y cómo usarlos para hacer miembros privados.


35
2018-02-01 08:48



La clave, según tengo entendido, es que Javascript tiene un alcance del nivel de función frente al alcance del bloque C más común.

Aquí hay un buen artículo sobre el tema.


28
2018-05-15 17:38



En "Javascript 1.7" (la extensión de Mozilla para Javascript) también se pueden declarar variables de alcance de bloque con let declaración:

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4

23
2018-04-06 11:19



La idea de determinar el alcance en JavaScript cuando fue diseñado originalmente por Brendan Eich vino de la HyperCard Lenguaje de escritura HyperTalk.

En este lenguaje, las pantallas se hicieron de forma similar a una pila de tarjetas de índice. Hubo una carta 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. Cada tarjeta colocada en la parte superior tenía su propio contenido que tenía prioridad sobre la anterior, pero aún así tenía acceso a las cartas anteriores, si así lo deseaba.

Así es exactamente como se diseña el sistema de scoping de JavaScript. Simplemente tiene diferentes nombres. Las tarjetas en JavaScript son conocidas como Contextos de ejecuciónECMA. Cada uno de estos contextos contiene tres partes principales. Un entorno variable, un entorno léxico y un enlace. Volviendo a la referencia de las cartas, el entorno léxico contiene todo el contenido de las cartas anteriores que están 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 indicará el objeto que lo contiene. A veces, los ámbitos o contextos de ejecución cambian sin que el objeto que los contiene cambie, como en una función declarada donde el objeto que los contiene puede window o una función de constructor

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.

Entonces esa es la explicación técnica. En la práctica, es importante recordar que en JavaScript

  • Los ámbitos son técnicamente "Contextos de ejecución"
  • Los contextos forman una pila de entornos donde se almacenan las variables
  • La parte superior de la pila tiene prioridad (la parte inferior es el contexto global)
  • Cada función crea un contexto de ejecución (pero no siempre es nuevo este enlace)

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. Se definen por el contexto externo, el contexto en la función invocada inmediatamente llamada por var seis, y el contexto en la función devuelta dentro de la función invocada inmediatamente por var seis.

yo) 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 entorno léxico de a = 6 y ese es el valor al que se hace referencia en la alerta cuando se llama.

enter image description here


18
2017-09-14 20:29