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?
javascript
Dan
fonte
fonte
Respostas:
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:
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
window
navegadores (ou viathis
escopo global, em código não estrito). Ao contrário de outras propriedades, a propriedade não pode ser removida viadelete
.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
var
acontece" abaixo).Observe que no IE8 e versões anteriores, a propriedade criada em
window
não é enumerável (não aparece nasfor..in
instruçõ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
,const
e as funções criados porclass
) 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 à
let
instrução. Quando a execução atinge alet
instrução, a variável fica acessível. (Veja "Quandolet
econst
acontece" abaixo.)# 1.2
const a = 0;
Cria uma constante global, que não é uma propriedade do objeto global.
const
é exatamente comolet
exceto que você deve fornecer um inicializador (a= value
parte), e você não pode alterar o valor da constante uma vez que é criado. Nos bastidores, é exatamente assim,let
mas com uma bandeira na ligação do identificador dizendo que seu valor não pode ser alterado. O usoconst
faz três coisas para você:# 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..in
instruções). Isso é estranho, principalmente considerando o número 3 abaixo.# 3
window.a = 0;
Isso cria uma propriedade explicitamente no objeto global, usando o
window
global que se refere ao objeto global (em navegadores; alguns ambientes que não são de navegador têm uma variável global equivalente, comoglobal
no 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
this
vez do globalwindow
. Porém, isso não funcionará no modo estrito, porque no código global do modo estritothis
não há uma referência ao objeto global (ele tem o valorundefined
).Excluindo propriedades
O que quero dizer com "excluindo" ou "removendo"
a
? Exatamente isso: Removendo a propriedade (inteiramente) por meio dadelete
palavra-chave:delete
remove completamente uma propriedade de um objeto. Você não pode fazer isso com as propriedades adicionadaswindow
indiretamente viavar
, odelete
é 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
window
objeto, 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 dowindow
objeto, você deve estar na defensiva: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
window
objeto 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 dewindow
propriedades, sujeitas às regras acima.Quando
var
aconteceAs variáveis definidas por meio da
var
instruçã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 davar
instrução.Isso pode ser confuso, então vamos dar uma olhada:
Exemplo ao vivo:
Mostrar snippet de código
Como você pode ver, o símbolo
foo
é definido antes da primeira linha, mas o símbolobar
não é. Onde está avar 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 "var
içamento" porque avar foo
peça é movida ("içada") para a parte superior do escopo, mas afoo = "f"
peça é deixada em seu local original. (Veja Pobre mal entendidovar
no meu pequeno blog anêmico.)Quando
let
econst
acontecerlet
econst
são diferentes devar
duas 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çãolet
ouconst
seja alcançada.Então, enquanto isso é executado:
Isso gera um erro:
As outras duas maneiras que diferem
let
e que não são realmente relevantes para a questão são:const
var
var
sempre 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), maslet
econst
se aplicam apenas dentro do bloco onde eles aparecem. Ou seja,var
tem a função (ou global) escopo, maslet
econst
tem escopo de bloco.recorrente
var a
no mesmo contexto é inofensivo, mas se você tiverlet a
(ouconst a
), ter outrolet a
ou aconst a
ou avar a
é um erro de sintaxe.Aqui está um exemplo demonstrando que
let
econst
entra em vigor imediatamente no bloco antes que qualquer código desse bloco seja executado, mas não está acessível até a instruçãolet
ouconst
:Observe que o segundo
console.log
falha, em vez de acessar oa
de fora do bloco.Fora do tópico: Evite sobrecarregar o objeto global (
window
)O
window
objeto 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 owindow
objeto. (Frequentemente não exporto nenhum símbolo para owindow
objeto.) 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: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).
fonte
window['a']=0
para deixar claro que estou usando a janela como um mapa? éwindow
especial de tal forma que alguns navegadores não permitem isso e me forçam a usarwindow.a
?window.a = 0;
só funciona em ambientes de navegador e apenas por convenção. Binding o objeto global para uma variável chamadawindow
não está no ES Spec e assim não vai funcionar, por exemplo, V8 ou Node.js, enquantothis.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á passarthis
como um parâmetro nomeadowindow
ouglobal
obter uma referência direta ao objeto global.var a = 0;
automaticamente se torna uma propriedade do objeto global. Se eu declararvar b = 0;
em uma declaração de função, ela também será propriedade de algum objeto subjacente?Mantendo simples:
O código acima fornece uma variável de escopo global
Esse código fornecerá uma variável a ser usada no escopo atual e, sob ela
Isso geralmente é igual à variável global.
fonte
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.Existe um objeto global no qual todos os vars são pendurados por padrão? por exemplo: 'declaração globals.noVar'
fonte
window.*
declaração. Essa declaração parece mais segura contra copiar e colar seu código e também limpa.Com base na excelente resposta de TJ Crowder : ( Fora de tópico: Evite a confusão
window
)Este é um exemplo de sua ideia:
Html
init.js (Com base nesta resposta )
script.js
Aqui está o plnkr . Espero que ajude!
fonte
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
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
fonte
delete
umvar
).var
. Eles são mecanismos completamente diferentes que têm praticamente o mesmo resultado prático. :-)var
pula para a parada do escopo.