¿Diferencia entre sintaxis de declaración de variables en Javascript (incluidas las variables globales)?

292

¿Hay alguna diferencia entre declarar una variable?

var a=0; //1

...de esta manera:

a=0; //2

...o:

window.a=0; //3

en alcance global?

Dan
fuente
2
AFAIK var a = 0; no funciona en IE cuando se accede a la variable a través de otro archivo js externo que se declara en otro archivo js
Aivan Monceller
No sé acerca de window.a pero las otras 2 formas son las mismas en alcance global.
programador
1
@AivanMonceller realmente? enlace por favor.
Raynos
@Raynos, lo experimento en mi propio sitio web. IE6 para ser específico. No pude hacer que aparezca mi var enum que está en un archivo js externo y lo estoy haciendo referencia como un JavaScript en línea en un archivo html
Aivan Monceller
@Ashwini En el ámbito global, la ventana es el objeto global (en los navegadores). var a = 1; console.log (a); console.log (win
leebriggs

Respuestas:

557

Sí, hay un par de diferencias, aunque en términos prácticos no suelen ser grandes.

Hay una cuarta forma, y ​​a partir de ES2015 (ES6) hay dos más. Agregué la cuarta forma al final, pero inserté las formas ES2015 después del # 1 (verá por qué), por lo que tenemos:

var a = 0;     // 1
let a = 0;     // 1.1 (new with ES2015)
const a = 0;   // 1.2 (new with ES2015)
a = 0;         // 2
window.a = 0;  // 3
this.a = 0;    // 4

Esas declaraciones explicadas

# 1 var a = 0;

Esto crea una variable global que también es una propiedad del objeto global , al que accedemos como windowen los navegadores (o mediante thisun alcance global, en código no estricto). A diferencia de otras propiedades, la propiedad no se puede eliminar mediante delete.

En términos de especificación, crea un enlace de identificador en el registro de entorno del objeto para el entorno global . Eso lo convierte en una propiedad del objeto global porque el objeto global es donde se guardan los enlaces de identificador para el registro de entorno del objeto del entorno global. Esta es la razón por la cual la propiedad no se puede eliminar: no es solo una propiedad simple, es un enlace de identificador.

El enlace (variable) se define antes de que se ejecute la primera línea de código (consulte "Cuándo varsucede" a continuación).

Tenga en cuenta que en IE8 y versiones anteriores, la propiedad creada en windowno es enumerable (no aparece en las for..indeclaraciones). En IE9, Chrome, Firefox y Opera, es enumerable.


# 1.1 let a = 0;

Esto crea una variable global que no es una propiedad del objeto global. Esto es algo nuevo a partir de ES2015.

En términos de especificación, crea un enlace de identificador en el Registro de entorno declarativo para el entorno global en lugar del registro de entorno del objeto . El medio ambiente mundial es el único que tiene una fracción de Medio Ambiente de registro, uno para todo el material antiguo que va en el objeto global (el objeto de Medio Ambiente Record) y otra para todas las cosas nuevas ( let, consty las funciones creadas por class) que no lo hacen ir al objeto global.

El enlace se crea antes de que se ejecute cualquier código paso a paso en su bloque adjunto (en este caso, antes de que se ejecute cualquier código global), pero no es accesible de ninguna manera hasta que la ejecución paso a paso llegue a la letdeclaración. Una vez que la ejecución alcanza la letdeclaración, la variable es accesible. (Ver "Cuándo lety constsuceder" a continuación).


# 1.2 const a = 0;

Crea una constante global, que no es una propiedad del objeto global.

constes exactamente como, letexcepto que debe proporcionar un inicializador (la = valueparte), y no puede cambiar el valor de la constante una vez que se crea. Debajo de las cubiertas, es exactamente igual letpero con una bandera en el enlace del identificador que dice que su valor no se puede cambiar. Usar consthace tres cosas por ti:

  1. Lo convierte en un error de tiempo de análisis si intenta asignar a la constante.
  2. Documenta su naturaleza inmutable para otros programadores.
  3. Permite que el motor de JavaScript se optimice sobre la base de que no cambiará.

# 2 a = 0;

Esto crea una propiedad en el objeto global implícitamente . Como es una propiedad normal, puede eliminarla. Recomiendo no hacer esto, puede no estar claro para cualquiera que lea su código más tarde. Si usa el modo estricto de ES5, hacer esto (asignar a una variable inexistente) es un error. Es una de varias razones para usar el modo estricto.

Y curiosamente, nuevamente en IE8 y versiones anteriores, la propiedad creada no es enumerable (no aparece en las for..indeclaraciones). Eso es extraño, particularmente dado el n. ° 3 a continuación.


# 3 window.a = 0;

Esto crea una propiedad en el objeto global explícitamente, utilizando el windowglobal que se refiere al objeto global (en los navegadores; algunos entornos que no son del navegador tienen una variable global equivalente, como globalen NodeJS). Como es una propiedad normal, puede eliminarla.

Esta propiedad es enumerable, en IE8 y anteriores, y en cualquier otro navegador que haya probado.


# 4 this.a = 0;

Exactamente como el n. ° 3, excepto que estamos haciendo referencia al objeto global en thislugar del global window. Sin embargo, esto no funcionará en modo estricto, porque en el código global en modo estricto, thisno tiene una referencia al objeto global (en su lugar tiene el valor undefined).


Eliminar propiedades

¿Qué quiero decir con "eliminar" o "eliminar" a? Exactamente eso: Eliminar la propiedad (completamente) a través de la deletepalabra clave:

window.a = 0;
display("'a' in window? " + ('a' in window)); // displays "true"
delete window.a;
display("'a' in window? " + ('a' in window)); // displays "false"

deleteelimina completamente una propiedad de un objeto. No puede hacer eso con las propiedades agregadas windowindirectamente a través de var, deletese ignora silenciosamente o produce una excepción (dependiendo de la implementación de JavaScript y si está en modo estricto).

Advertencia : IE8 nuevamente (y presumiblemente antes, e IE9-IE11 en el modo roto de "compatibilidad"): no le permitirá eliminar las propiedades del windowobjeto, incluso cuando se le debe permitir. Peor aún, arroja una excepción cuando lo intentas ( prueba este experimento en IE8 y en otros navegadores). Entonces, al eliminar del windowobjeto, debes estar a la defensiva:

try {
    delete window.prop;
}
catch (e) {
    window.prop = undefined;
}

Eso intenta eliminar la propiedad, y si se lanza una excepción, hace lo mejor y establece la propiedad en undefined.

Esto solo se aplica al windowobjeto, y solo (hasta donde yo sé) a IE8 y anteriores (o IE9-IE11 en el modo de "compatibilidad" roto). Otros navegadores están bien con la eliminación de windowpropiedades, sujeto a las reglas anteriores.


Cuando varsucede

Las variables definidas a través de la vardeclaración se crean antes de ejecutar cualquier código paso a paso en el contexto de ejecución, por lo que la propiedad existe mucho antes de la vardeclaración.

Esto puede ser confuso, así que echemos un vistazo:

display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo);          // displays "undefined"
display("bar in window? " + ('bar' in window)); // displays "false"
display("window.bar = " + window.bar);          // displays "undefined"
var foo = "f";
bar = "b";
display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo);          // displays "f"
display("bar in window? " + ('bar' in window)); // displays "true"
display("window.bar = " + window.bar);          // displays "b"

Ejemplo en vivo:

Como puede ver, el símbolo foose define antes de la primera línea, pero el símbolo barno. Donde está la var foo = "f";declaración, realmente hay dos cosas: definir el símbolo, que ocurre antes de que se ejecute la primera línea de código; y haciendo una asignación a ese símbolo, que sucede donde está la línea en el flujo paso a paso. Esto se conoce como " varelevación" porque la var fooparte se mueve ("eleva") a la parte superior del alcance, pero la foo = "f"parte se deja en su ubicación original. (Ver Pobre mal entendidovar en mi pequeño blog anémico).


Cuando lety constsuceder

lety constson diferentes de varias varmaneras. La forma que es relevante para la pregunta es que, aunque el enlace que definen se crea antes de que se ejecute un código paso a paso, no es accesible hasta que se alcanza la declaración leto const.

Entonces, mientras esto se ejecuta:

display(a);    // undefined
var a = 0;
display(a);    // 0

Esto arroja un error:

display(a);    // ReferenceError: a is not defined
let a = 0;
display(a);

Las otras dos formas que difieren lety que no son realmente relevantes para la pregunta son:constvar

  1. varSiempre se aplica a la totalidad del contexto de ejecución (a lo largo código global, o en todo código de función en la función donde aparece), pero lety constaplicación exclusivamente en el bloque en el que aparecen. Es decir, vartiene la función (o global) alcance, pero lety consttiene ámbito de bloque.

  2. Repetir var aen el mismo contexto es inofensivo, pero si tiene let a(o const a), tener otro let ao a const ao a var aes un error de sintaxis.

Aquí hay un ejemplo que demuestra eso lety constsurte efecto inmediatamente en su bloque antes de que se ejecute cualquier código dentro de ese bloque, pero no se puede acceder hasta la instrucción letor const:

var a = 0;
console.log(a);
if (true)
{
  console.log(a); // ReferenceError: a is not defined
  let a = 1;
  console.log(a);
}

Tenga en cuenta que el segundo console.logfalla, en lugar de acceder adesde fuera del bloque.


Fuera de tema: evite abarrotar el objeto global ( window)

El windowobjeto se vuelve muy, muy abarrotado de propiedades. Siempre que sea posible, recomiendo no agregar nada al desorden. En cambio, envuelva sus símbolos en un pequeño paquete y exporte como máximo un símbolo al windowobjeto. (Con frecuencia no exporto ningún símbolo al windowobjeto). Puede usar una función para contener todo su código para contener sus símbolos, y esa función puede ser anónima si desea:

(function() {
    var a = 0; // `a` is NOT a property of `window` now

    function foo() {
        alert(a);   // Alerts "0", because `foo` can access `a`
    }
})();

En ese ejemplo, definimos una función y la ejecutamos de inmediato ( ()al final).

Una función utilizada de esta manera con frecuencia se denomina función de alcance . Las funciones definidas dentro de la función de alcance pueden acceder a las variables definidas en la función de alcance porque son cierres sobre esos datos (ver: Los cierres no son complicados en mi pequeño blog anémico).

TJ Crowder
fuente
¿Qué puedo hacer window['a']=0para dejar en claro que estoy usando la ventana como mapa? ¿Es windowespecial tal que algunos navegadores no permiten esto y me obligan a usar window.a?
Jayen
Una nota sobre el n . ° 3 que probablemente valga la pena aclarar: window.a = 0;solo funciona en entornos de navegador y solo por convención. La vinculación del objeto global a una variable denominada windowno está en la especificación ES y, por lo tanto, no funcionará, por ejemplo, en V8 o Node.js, mientras que this.a = 0;(cuando se invoca en el contexto de ejecución global) funcionará en cualquier entorno ya que la especificación especifica que debe haber un objeto global. Si ajusta su código en un IIFE como en la sección Fuera de tema , puede pasarlo thiscomo un parámetro llamado windowo globalpara obtener una referencia directa al objeto global.
Sherlock_HJ
@ Sherlock_HJ: he agregado "en los navegadores"; eso también aparece anteriormente en la respuesta, pero lo agregué en caso de que la gente salte a eso. Está en la especificación ahora ; Si bien es solo de pasada, no encontrará un navegador que no lo haga. Estoy un poco sorprendido de que no está en el Anexo B .
TJ Crowder
@TJCrowder, entonces, una variable global declarada con var a = 0;automáticamente se convierte en una propiedad del objeto global. Si declaro var b = 0;dentro de una declaración de función, ¿será también una propiedad de algún objeto subyacente?
ezpresso
@ezpresso: No y sí. Se convierten en propiedades de un objeto ( EnvironmentRecord del VariableEnvironment del ExecutionContext donde aparecen; detalles aquí y aquí ), pero no hay forma de acceder directamente a ese objeto desde el código del programa.
TJ Crowder
40

Manteniéndolo simple:

a = 0

El código anterior proporciona una variable de alcance global

var a = 0;

Este código dará una variable para ser utilizada en el alcance actual, y debajo de ella

window.a = 0;

Esto generalmente es lo mismo que la variable global.

Umair Jabbar
fuente
Sus declaraciones "El código anterior proporciona una variable de alcance global" y "Este código proporcionará una variable para usar en el alcance actual y debajo de él" , en conjunto, sugieren que no puede usar la primera línea y acceder a bajo el Alcance actual. Usted puede. Además, su uso de "variable global" está un poco fuera de lugar: los dos lugares que dice "variable global" no son más globales que el lugar donde no lo dice.
TJ Crowder
global en sí mismo significa que puede acceder / leer / escribir la variable en cualquier lugar, incluido el lugar donde mencioné el alcance actual, eso es tan obvio. Y si sugiere que window.a y 'a' no serán globales en el script, entonces está 100% equivocado.
Umair Jabbar
3
@Umair: "global en sí mismo significa que puede acceder / leer / escribir la variable en cualquier lugar" Derecha. Una vez más, parece que estás llamando al primero y al último como más "global" que el medio, que por supuesto no lo son.
TJ Crowder
44
el medio se considera que se usa dentro de una función, todos serían iguales si se usaran bajo el alcance principal. usar var dentro de una función fue mi suposición
Umair Jabbar
44
@Umair: "usar var dentro de una función fue mi suposición" Ah, está bien. Pero esa no es la pregunta. La pregunta dice muy claramente "en alcance global" . Si va a cambiar el supuesto (que es lo suficientemente justo, para expandir y explicar un punto más general), deberá tener claro que eso es lo que está haciendo en su respuesta.
TJ Crowder
10
<title>Index.html</title>
<script>
    var varDeclaration = true;
    noVarDeclaration = true;
    window.hungOnWindow = true;
    document.hungOnDocument = true;
</script>
<script src="external.js"></script>

/* external.js */

console.info(varDeclaration == true); // could be .log, alert etc
// returns false in IE8

console.info(noVarDeclaration == true); // could be .log, alert etc
// returns false in IE8

console.info(window.hungOnWindow == true); // could be .log, alert etc
// returns true in IE8

console.info(document.hungOnDocument == true); // could be .log, alert etc
// returns ??? in IE8 (untested!)  *I personally find this more clugy than hanging off window obj

¿Hay algún objeto global del que se cuelguen todos los vars de forma predeterminada? por ejemplo: 'declaración globals.noVar'

Cody
fuente
Muy buena exploración. La guía definitiva para usar la window.*declaración. Esta declaración parece más segura contra copiar y pegar su código, y también es clara.
Dan
7

Basado en la excelente respuesta de TJ Crowder : ( Fuera de tema: Evita el desordenwindow )

Este es un ejemplo de su idea:

HTML

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="init.js"></script>
    <script type="text/javascript">
      MYLIBRARY.init(["firstValue", 2, "thirdValue"]);
    </script>
    <script src="script.js"></script>
  </head>

  <body>
    <h1>Hello !</h1>
  </body>    
</html>

init.js (Basado en esta respuesta )

var MYLIBRARY = MYLIBRARY || (function(){
    var _args = {}; // private

    return {
        init : function(Args) {
            _args = Args;
            // some other initialising
        },
        helloWorld : function(i) {
            return _args[i];
        }
    };
}());

script.js

// Here you can use the values defined in the html as if it were a global variable
var a = "Hello World " + MYLIBRARY.helloWorld(2);

alert(a);

Aquí está el plnkr . Espero que ayude!

robe007
fuente
5

En el ámbito global no hay diferencia semántica.

Pero realmente debe evitarlo a=0ya que establece un valor en una variable no declarada.

También use cierres para evitar editar el alcance global

(function() {
   // do stuff locally

   // Hoist something to global scope
   window.someGlobal = someLocal
}());

Siempre use cierres y siempre iremos al alcance global cuando sea absolutamente necesario. De todos modos, debe utilizar el manejo de eventos asíncronos para la mayoría de sus comunicaciones.

Como @AvianMoncellor mencionó, hay un error de IE que var a = foosolo declara un alcance global para el archivo. Este es un problema con el notorio intérprete roto de IE. Este error suena familiar, por lo que probablemente sea cierto.

Así que quédate con window.globalName = someLocalpointer

Raynos
fuente
2
"En el ámbito global no hay diferencia semántica". En realidad, hay una enorme diferencia semántica, los mecanismos por los que la propiedad se define son completamente diferentes - pero en términos prácticos, esto se reduce a sólo una pequeña real diferencia (en que no se puede deleteuna var).
TJ Crowder
@TJ Crowder No lo sabía. Pensé que la declaración variable establecía propiedades en el objeto variable. No sabía que no podían eliminarse.
Raynos
Sip. También se definen anteriormente si los usa var. Son mecanismos completamente diferentes que tienen el mismo resultado práctico. :-)
TJ Crowder
@TJ Crowder Olvidé mencionar que varsalta al límite de alcance.
Raynos