Diferença entre sintaxe de declaração de variável em Javascript (incluindo variáveis ​​globais)?

292

Existe alguma diferença entre declarar uma variável:

var a=0; //1

...deste jeito:

a=0; //2

...ou:

window.a=0; //3

no escopo global?

Dan
fonte
2
AFAIK var a = 0; não funciona no IE ao acessar a variável através de outro arquivo js externos que é declarado em outro arquivo js
Aivan Monceller
Eu não sei sobre window.a, mas as outras duas maneiras são as mesmas no escopo global.
programador
1
@AivanMonceller realmente? O link, por favor.
Raynos
@ Raynos, eu experimento isso no meu próprio site. IE6 para ser específico. Eu não poderia começar o meu var enum a aparecer, que está em um arquivo JS externo e eu estou fazendo referência a ele como um javascript em linha em um arquivo html
Aivan Monceller
@ Ashshini No escopo global, window é o objeto global (nos navegadores). var a = 1; console.log (a); console.log (win
leebriggs

Respostas:

557

Sim, existem algumas diferenças, embora em termos práticos geralmente não sejam grandes.

Existe uma quarta maneira, e a partir do ES2015 (ES6) há mais duas. Eu adicionei a quarta maneira no final, mas inseri as formas do ES2015 após o 1 (você verá o porquê), então temos:

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

Essas declarações explicadas

# 1 var a = 0;

Isso cria uma variável global que também é propriedade do objeto global , ao qual acessamos como windownavegadores (ou via thisescopo global, em código não estrito). Ao contrário de outras propriedades, a propriedade não pode ser removida via delete.

Em termos de especificação, ele cria uma ligação de identificador no objeto Registro de Ambiente para o ambiente global . Isso o torna uma propriedade do objeto global porque é no objeto global que são mantidas as ligações de identificador para o registro de ambiente do objeto do ambiente global. É por isso que a propriedade não pode ser excluída: não é apenas uma propriedade simples, é uma ligação de identificador.

A ligação (variável) é definida antes da execução da primeira linha de código (consulte "Quando varacontece" abaixo).

Observe que no IE8 e versões anteriores, a propriedade criada em windownão é enumerável (não aparece nas for..ininstruções). No IE9, Chrome, Firefox e Opera, é enumerável.


# 1.1 let a = 0;

Isso cria uma variável global que não é uma propriedade do objeto global. Isso é algo novo a partir do ES2015.

Em termos de especificação, ele cria uma ligação de identificador no Declarative Environment Record para o ambiente global, em vez do objeto Environment Record. O ambiente global é o único a ter uma divisão Ambiente Record, um para todo o material antigo que se passa o objeto global (o objeto Ambiente Record) e outro para todo o material novo ( let, conste as funções criados por class) que não fazer vá no objeto global.

A ligação é criada antes da execução de qualquer código passo a passo em seu bloco anexo (neste caso, antes da execução de qualquer código global), mas não é acessível de maneira alguma até que a execução passo a passo chegue à letinstrução. Quando a execução atinge a letinstrução, a variável fica acessível. (Veja "Quando lete constacontece" abaixo.)


# 1.2 const a = 0;

Cria uma constante global, que não é uma propriedade do objeto global.

consté exatamente como letexceto que você deve fornecer um inicializador (a = valueparte), e você não pode alterar o valor da constante uma vez que é criado. Nos bastidores, é exatamente assim, letmas com uma bandeira na ligação do identificador dizendo que seu valor não pode ser alterado. O uso constfaz três coisas para você:

  1. Torna um erro de tempo de análise se você tentar atribuir à constante.
  2. Documenta sua natureza imutável para outros programadores.
  3. Permite que o mecanismo JavaScript seja otimizado com base em que não será alterado.

# 2 a = 0;

Isso cria uma propriedade no objeto global implicitamente . Como é uma propriedade normal, você pode excluí-la. Eu recomendo não fazer isso, pois pode não estar claro para quem ler seu código posteriormente. Se você usar o modo estrito do ES5, fazer isso (atribuir a uma variável inexistente) é um erro. É uma das várias razões para usar o modo estrito.

E, curiosamente, novamente no IE8 e versões anteriores, a propriedade criada não é enumerável (não aparece nas for..ininstruções). Isso é estranho, principalmente considerando o número 3 abaixo.


# 3 window.a = 0;

Isso cria uma propriedade explicitamente no objeto global, usando o windowglobal que se refere ao objeto global (em navegadores; alguns ambientes que não são de navegador têm uma variável global equivalente, como globalno NodeJS). Como é uma propriedade normal, você pode excluí-la.

Essa propriedade é enumerável, no IE8 e versões anteriores, e em todos os outros navegadores que tentei.


# 4 this.a = 0;

Exatamente como o nº 3, exceto que estamos referenciando o objeto global em thisvez do global window. Porém, isso não funcionará no modo estrito, porque no código global do modo estrito thisnão há uma referência ao objeto global (ele tem o valor undefined).


Excluindo propriedades

O que quero dizer com "excluindo" ou "removendo" a? Exatamente isso: Removendo a propriedade (inteiramente) por meio da deletepalavra-chave:

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

deleteremove completamente uma propriedade de um objeto. Você não pode fazer isso com as propriedades adicionadas windowindiretamente via var, o deleteé silenciosamente ignorado ou gera uma exceção (dependendo da implementação do JavaScript e se você está no modo estrito).

Aviso : IE8 novamente (e presumivelmente mais cedo, e IE9-IE11 no modo "compatibilidade" quebrado): não permitirá que você exclua propriedades do windowobjeto, mesmo quando lhe for permitido. Pior, isso gera uma exceção quando você tenta ( tente esta experiência no IE8 e em outros navegadores). Portanto, ao excluir do windowobjeto, você deve estar na defensiva:

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

Isso tenta excluir a propriedade e, se uma exceção for lançada, ela fará a próxima melhor coisa e definirá a propriedade undefined.

Isso se aplica apenas ao windowobjeto e apenas (tanto quanto eu sei) ao IE8 e versões anteriores (ou IE9-IE11 no modo "compatibilidade" quebrado). Outros navegadores estão bem com a exclusão de windowpropriedades, sujeitas às regras acima.


Quando varacontece

As variáveis ​​definidas por meio da varinstrução são criadas antes da execução de qualquer código passo a passo no contexto de execução e, portanto, a propriedade existe bem antes da varinstrução.

Isso pode ser confuso, então vamos dar uma olhada:

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"

Exemplo ao vivo:

Como você pode ver, o símbolo fooé definido antes da primeira linha, mas o símbolo barnão é. Onde está a var foo = "f";declaração, há realmente duas coisas: definir o símbolo, o que acontece antes da execução da primeira linha de código; e fazendo uma atribuição a esse símbolo, o que acontece onde a linha está no fluxo passo a passo. Isso é conhecido como " variçamento" porque a var foopeça é movida ("içada") para a parte superior do escopo, mas a foo = "f"peça é deixada em seu local original. (Veja Pobre mal entendidovar no meu pequeno blog anêmico.)


Quando lete constacontecer

lete constsão diferentes de varduas maneiras. A maneira que é relevante para a pergunta é que, embora a ligação que eles definem seja criada antes da execução de qualquer código passo a passo, ela não estará acessível até que a instrução letou constseja alcançada.

Então, enquanto isso é executado:

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

Isso gera um erro:

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

As outras duas maneiras que diferem lete que não são realmente relevantes para a questão são:constvar

  1. varsempre se aplica a todo o contexto de execução (em todo código global, ou durante todo código de função na função de onde ele aparece), mas lete constse aplicam apenas dentro do bloco onde eles aparecem. Ou seja, vartem a função (ou global) escopo, mas lete consttem escopo de bloco.

  2. recorrente var a no mesmo contexto é inofensivo, mas se você tiver let a(ou const a), ter outro let aou a const aou a var aé um erro de sintaxe.

Aqui está um exemplo demonstrando que let e constentra em vigor imediatamente no bloco antes que qualquer código desse bloco seja executado, mas não está acessível até a instrução letou const:

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

Observe que o segundo console.logfalha, em vez de acessar o ade fora do bloco.


Fora do tópico: Evite sobrecarregar o objeto global ( window)

O windowobjeto fica muito, muito cheio de propriedades. Sempre que possível, é altamente recomendável não adicionar à bagunça. Em vez disso, embrulhe seus símbolos em um pequeno pacote e exporte no máximo um símbolo para o windowobjeto. (Frequentemente não exporto nenhum símbolo para o windowobjeto.) Você pode usar uma função para conter todo o seu código, a fim de conter seus símbolos, e essa função pode ser anônima, se você quiser:

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

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

Nesse exemplo, definimos uma função e a executamos imediatamente (a ()no final).

Uma função usada dessa maneira é freqüentemente chamada de função de escopo . As funções definidas na função de escopo podem acessar variáveis ​​definidas na função de escopo porque são fechamentos sobre esses dados (consulte: Encerramentos não são complicados no meu pequeno blog anêmico).

TJ Crowder
fonte
posso fazer window['a']=0para deixar claro que estou usando a janela como um mapa? é windowespecial de tal forma que alguns navegadores não permitem isso e me forçam a usar window.a?
Jayen
Uma observação no # 3 que provavelmente vale a pena esclarecer: window.a = 0;só funciona em ambientes de navegador e apenas por convenção. Binding o objeto global para uma variável chamada windownão está no ES Spec e assim não vai funcionar, por exemplo, V8 ou Node.js, enquanto this.a = 0;(quando invocada no contexto de execução global) irá funcionar em qualquer ambiente, desde a especificação não especificar que deve haver um objeto global. Se estiver quebrando seu código em um IIFE como na seção Fora de tópico , você poderá passar thiscomo um parâmetro nomeado windowou globalobter uma referência direta ao objeto global.
21416 Sherlock_HJ
@ Sherlock_HJ: adicionei "nos navegadores"; isso também está no início da resposta, mas eu o adicionei no caso de as pessoas pularem para isso. Está na especificação agora ; enquanto estiver apenas de passagem, você não encontrará um navegador que não faça isso. Estou um pouco surpreso por não estar anexo B .
TJ Crowder
@TJCrowder, uma variável global declarada com var a = 0; automaticamente se torna uma propriedade do objeto global. Se eu declarar var b = 0;em uma declaração de função, ela também será propriedade de algum objeto subjacente?
Ezpresso # 15/16
@ezpresso: Não e sim. Eles se tornam propriedades de um objeto (o EnvironmentRecord do VariableEnvironment do ExecutionContext onde aparecem; detalhes aqui e aqui ), mas não há como acessar diretamente esse objeto a partir do código do programa.
TJ Crowder
40

Mantendo simples:

a = 0

O código acima fornece uma variável de escopo global

var a = 0;

Esse código fornecerá uma variável a ser usada no escopo atual e, sob ela

window.a = 0;

Isso geralmente é igual à variável global.

Umair Jabbar
fonte
Suas declarações "O código acima dá uma variável âmbito global" e "Este código vai dar uma variável a ser usada no escopo atual, e sob ele" , tomados em conjunto, sugerem que você não pode usar a primeira linha e acesso a sob o escopo atual. Você pode. Além disso, seu uso da "variável global" é um pouco complicado - os dois lugares que você diz "variável global" não são mais globais do que o lugar que você não diz.
TJ Crowder
global em si significa que você pode acessar / ler / gravar a variável em qualquer lugar, incluindo o local em que mencionei o escopo atual, que é tão óbvio. E se você sugerir que window.a e 'a' não serão globais no script, estará 100% errado.
Umair Jabbar
3
@Umair: "global em si significa que você pode acessar / ler / escrever a variável em qualquer lugar" . Mais uma vez, você parece chamar o primeiro e o último como mais "globais" que o meio, o que é claro que não são.
TJ Crowder
4
o do meio é considerado usado dentro de uma função, todos seriam iguais se usados ​​no escopo principal. usando var dentro de uma função foi minha suposição
Umair Jabbar
4
@Umair: "usando var dentro de uma função foi minha suposição" Ah, tudo bem. Mas essa não é a questão. A pergunta diz claramente "no escopo global" . Se você vai mudar a suposição (o que é justo o suficiente, para expandir e explicar um ponto mais geral), você precisará deixar claro que é isso que está fazendo na sua resposta.
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

Existe um objeto global no qual todos os vars são pendurados por padrão? por exemplo: 'declaração globals.noVar'

Cody
fonte
Exploração muito agradável. O guia definitivo para usar a window.*declaração. Essa declaração parece mais segura contra copiar e colar seu código e também limpa.
21412 Dan
7

Com base na excelente resposta de TJ Crowder : ( Fora de tópico: Evite a confusãowindow )

Este é um exemplo de sua ideia:

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 (Com base nesta resposta )

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);

Aqui está o plnkr . Espero que ajude!

robe007
fonte
5

No escopo global, não há diferença semântica.

Mas você realmente deve evitar a=0 desde que você defina um valor para uma variável não declarada.

Use também fechamentos para evitar a edição do escopo global

(function() {
   // do stuff locally

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

Sempre use fechos e iça para o escopo global quando for absolutamente necessário. Você deve usar a manipulação de eventos assíncrona para a maior parte de sua comunicação.

Como o @AvianMoncellor mencionou, há um bug do IE com var a = foo apenas uma declaração global para o escopo do arquivo. Esse é um problema com o notório intérprete quebrado do IE. Esse bug parece familiar, provavelmente é verdade.

Então fique com window.globalName = someLocalpointer

Raynos
fonte
2
"No âmbito global, não há diferença semântica." Na verdade, há uma enorme diferença semântica, os mecanismos pelos quais a propriedade fica definidos são completamente diferentes - mas em termos práticos tudo se resume a apenas uma pequena real diferença (em que não é possível deleteum var).
TJ Crowder
@TJ Crowder Eu não sabia disso. Eu pensei que a declaração variável estava definindo propriedades no objeto variável. Não sabia que não era possível excluir esses itens.
Raynos
Sim. Eles também são definidos anteriormente se você usar var. Eles são mecanismos completamente diferentes que têm praticamente o mesmo resultado prático. :-)
TJ Crowder
@TJ Crowder Esqueci de mencionar que varpula para a parada do escopo.
Raynos