Qual é o escopo das variáveis em javascript? Eles têm o mesmo escopo dentro e não fora de uma função? Ou isso importa? Além disso, onde estão as variáveis armazenadas se definidas globalmente?
O JavaScript possui escopo e fechamentos lexicais (também chamados estáticos). Isso significa que você pode dizer o escopo de um identificador olhando o código-fonte.
Os quatro escopos são:
Global - visível por tudo
Função - visível dentro de uma função (e suas subfunções e blocos)
Bloco - visível dentro de um bloco (e seus sub-blocos)
Módulo - visível dentro de um módulo
Fora dos casos especiais do escopo global e do módulo, as variáveis são declaradas usando var(escopo da função), let(escopo do bloco) e const(escopo do bloco). A maioria das outras formas de declaração de identificador tem escopo de bloco no modo estrito.
Visão geral
Escopo é a região da base de código sobre a qual um identificador é válido.
Um ambiente lexical é um mapeamento entre nomes de identificadores e os valores associados a eles.
O escopo é formado por um aninhamento vinculado de ambientes lexicais, com cada nível no aninhamento correspondendo a um ambiente lexical de um contexto de execução ancestral.
Esses ambientes lexicais vinculados formam um escopo "cadeia". A resolução do identificador é o processo de busca ao longo dessa cadeia por um identificador correspondente.
A resolução do identificador ocorre apenas em uma direção: para o exterior. Dessa maneira, os ambientes lexicais externos não podem "ver" os ambientes lexicais internos.
Existem três fatores pertinentes para decidir o escopo de um identificador em JavaScript:
Algumas das maneiras pelas quais os identificadores podem ser declarados:
var, leteconst
Parâmetros da função
Parâmetro do bloco de captura
Declarações de função
Expressões de funções nomeadas
Propriedades implicitamente definidas no objeto global (isto é, falta varno modo não estrito)
import afirmações
eval
Alguns dos identificadores de locais podem ser declarados:
Contexto global
Corpo da função
Bloco ordinário
O topo de uma estrutura de controle (por exemplo, loop, if, while etc)
Corpo da estrutura de controle
Módulos
Estilos de declaração
var
Identificadores declarados usando vartêm escopo de função , além de quando são declarados diretamente no contexto global; nesse caso, eles são adicionados como propriedades no objeto global e têm escopo global. Existem regras separadas para seu uso em evalfunções.
deixe e const
Identificadores declarados usando lete consttêm escopo de bloco , exceto quando são declarados diretamente no contexto global; nesse caso, eles têm escopo global.
Nota: let, conste varestão todos içada . Isso significa que sua posição lógica de definição é a parte superior de seu escopo (bloco ou função). No entanto, as variáveis declaradas estão sendo usadas lete constnão podem ser lidas ou atribuídas até que o controle tenha passado o ponto de declaração no código-fonte. O período intermediário é conhecido como zona morta temporal.
function f(){function g(){
console.log(x)}let x =1
g()}
f()// 1 because x is hoisted even though declared with `let`!
Os nomes de parâmetros da função têm escopo definido para o corpo da função. Observe que há uma leve complexidade nisso. As funções declaradas como argumentos padrão fecham-se sobre a lista de parâmetros , e não o corpo da função.
Declarações de função
As declarações de função têm escopo de bloco no modo estrito e escopo de função no modo não estrito. Nota: o modo não estrito é um conjunto complicado de regras emergentes com base nas implementações históricas peculiares de diferentes navegadores.
Expressões de funções nomeadas
As expressões de funções nomeadas têm um escopo definido para elas mesmas (por exemplo, para fins de recursão).
Propriedades definidas implicitamente no objeto global
No modo não estrito, as propriedades implicitamente definidas no objeto global têm escopo global, porque o objeto global fica na parte superior da cadeia de escopo. No modo estrito, isso não é permitido.
avaliação
Nas evalcadeias, as variáveis declaradas usando varserão colocadas no escopo atual ou, se evalusadas indiretamente, como propriedades no objeto global.
Exemplos
A seguir irá lançar uma ReferenceError porque os nomes x, ye znão têm nenhum significado fora da função f.
function f(){var x =1let y =1const 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)
A seguir, será gerado um ReferenceError para ye z, mas não para x, porque a visibilidade de xnão é restringida pelo bloco. Blocos que definem os corpos de estruturas de controle como if, fore while, se comportam de forma semelhante.
{var x =1let y =1const 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
... por causa desse comportamento, você precisa ter cuidado ao fechar as variáveis declaradas usando varloops. Há apenas uma instância da variável xdeclarada aqui e ela fica logicamente fora do loop.
As seguintes impressões são impressas 5cinco vezes e depois são impressas 5pela sexta vez pela parte console.logexterna do loop:
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
A seguinte impressão é impressa undefinedporque xtem escopo de bloco. Os retornos de chamada são executados um por um assincronamente. Novo comportamento de letmeios variáveis que cada função anônima fechada sobre uma variável diferente chamada x(ao contrário do que teria feito com var), e assim inteiros 0através 4são impressos .:
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
O seguinte NÃO lançará a ReferenceErrorporque a visibilidade de xnão é restringida pelo bloco; no entanto, ela será impressa undefinedporque a variável não foi inicializada (devido à ifinstrução).
if(false){var x =1}
console.log(x)// here, `x` has been declared, but not initialised
Variáveis declaradas usando var, letou consttodos com escopo definido para módulos:
// module1.jsvar x =0exportfunction f(){}//module2.jsimport f from'module1.js'
console.log(x)// throws ReferenceError
A seguir, declararemos uma propriedade no objeto global, porque as variáveis declaradas usando vardentro do contexto global são adicionadas como propriedades ao objeto global:
var x =1
console.log(window.hasOwnProperty('x'))// true
Escopo é definido como a região lexical do código sobre a qual um identificador é válido.
No JavaScript, todo objeto de função possui uma [[Environment]]referência oculta que é uma referência ao ambiente lexical do contexto de execução (quadro da pilha) no qual foi criado.
Quando você invoca uma função, o [[Call]]método oculto é chamado. Este método cria um novo contexto de execução e estabelece um link entre o novo contexto de execução e o ambiente lexical do objeto de função. Isso é feito copiando o [[Environment]]valor no objeto de função, para um campo de referência externo no ambiente lexical do novo contexto de execução.
Observe que esse link entre o novo contexto de execução e o ambiente lexical do objeto de função é chamado de fechamento .
Assim, em JavaScript, o escopo é implementado através de ambientes lexicais vinculados em uma "cadeia" por referências externas. Essa cadeia de ambientes lexicais é chamada cadeia de escopo, e a resolução do identificador ocorre pesquisando-se na cadeia por um identificador correspondente.
Nem mesmo perto de ser abrangente, mas talvez esse seja o conjunto de truques de escopo Javascript que você precisa conhecer para ler com eficácia o javascript moderno.
Triptych
148
Uma resposta altamente cotada, sem saber por quê. É apenas um monte de exemplos sem explicação adequada, e parece confundir herança de protótipo (ou seja, resolução de propriedades) com a cadeia de escopo (ou seja, resolução variável). Uma explicação abrangente (e precisa) do escopo e da resolução de propriedades está nas notas de FAQ do comp.lang.javascript .
RobG 10/09/12
109
O @RobG é altamente classificado porque é útil e compreensível para uma ampla gama de programadores, apesar da menor catacresis. O link que você postou, embora útil para alguns profissionais, é incompreensível para a maioria das pessoas que escreve Javascript hoje. Sinta-se à vontade para corrigir qualquer problema de nomenclatura editando a resposta.
Triptych
7
@ Triptych - edito apenas respostas para corrigir pequenas coisas, não importantes. Alterar "escopo" para "propriedade" corrigirá o erro, mas não a questão de misturar herança e escopo sem uma distinção muito clara.
RobG 10/09/12
24
Se você definir uma variável no escopo externo e, em seguida, tiver uma instrução if, defina uma variável dentro da função com o mesmo nome, mesmo que se a ramificação não for alcançada, ela será redefinida. Um exemplo - jsfiddle.net/3CxVm
Chris S
233
Javascript usa cadeias de escopo para estabelecer o escopo de uma determinada função. Geralmente, há um escopo global e cada função definida possui seu próprio escopo aninhado. Qualquer função definida dentro de outra função tem um escopo local que está vinculado à função externa. É sempre a posição na fonte que define o escopo.
Um elemento na cadeia de escopo é basicamente um mapa com um ponteiro para seu escopo pai.
Ao resolver uma variável, o javascript inicia no escopo mais interno e pesquisa para fora.
Cadeias de escopo são outro termo para fechamentos [de memória] ... para quem lê aqui para aprender / entrar em javascript.
New Alexandria
108
Variáveis declaradas globalmente têm um escopo global. As variáveis declaradas em uma função têm escopo definido para essa função e sombream variáveis globais com o mesmo nome.
(Tenho certeza de que existem muitas sutilezas que os verdadeiros programadores JavaScript poderão apontar em outras respostas. Em particular, encontrei esta página sobre o que exatamente thissignifica a qualquer momento. Espero que este link mais introdutório seja suficiente para você começar .)
Tenho medo de começar a responder a essa pergunta. Como um programador Javascript real, sei com que rapidez a resposta pode ficar fora de controle. Artigos agradáveis.
Triptych
10
@ Triptych: Eu sei o que você quer dizer com as coisas ficando fora de controle, mas por favor, adicione uma resposta de qualquer maneira. Eu obtive o que foi dito acima apenas fazendo algumas pesquisas ... uma resposta escrita por alguém com experiência real provavelmente será melhor. Corrija qualquer resposta que esteja definitivamente errada!
911 Jon Skeet
4
De alguma forma, Jon Skeet é responsável pela MINHA resposta mais popular no Stack Overflow.
Tríptico
75
JavaScript da velha escola
Tradicionalmente, o JavaScript realmente tem apenas dois tipos de escopo:
Escopo Global : As variáveis são conhecidas em todo o aplicativo, desde o início do aplicativo (*)
Escopo Funcional : As variáveis são conhecidas dentro função em que são declaradas, desde o início da função (*)
Não vou elaborar isso, pois já existem muitas outras respostas que explicam a diferença.
Escopo do bloco : os identificadores são "conhecidos" na parte superior do escopo em que são declarados , mas não podem ser atribuídos ou desreferenciados (lidos) até depois da linha de sua declaração. Esse período intermediário é chamado de "zona morta temporal".
Como crio variáveis de escopo de bloco?
Tradicionalmente, você cria suas variáveis assim:
var myVariable ="Some text";
As variáveis do escopo do bloco são criadas assim:
let myVariable ="Some text";
Então, qual é a diferença entre escopo funcional e escopo de bloco?
Para entender a diferença entre o escopo funcional e o escopo do bloco, considere o seguinte código:
// i IS NOT known here// j IS NOT known here// k IS known here, but undefined// l IS NOT known herefunction 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 herefor(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 herefor(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
Aqui, podemos ver que nossa variável jé conhecida apenas no primeiro loop for, mas não antes e depois. No entanto, nossa variáveli é conhecida em toda a função.
Além disso, considere que as variáveis com escopo de bloco não são conhecidas antes de serem declaradas porque não são içadas. Também não é permitido redefinir a mesma variável com escopo de bloco dentro do mesmo bloco. Isso torna as variáveis com escopo do bloco menos propensas a erros do que as variáveis com escopo global ou funcional, que são içadas e que não produzem erros no caso de várias declarações.
É seguro usar variáveis de escopo de bloco hoje?
Se é seguro usar ou não hoje, depende do seu ambiente:
Se você estiver escrevendo o código JavaScript do servidor ( Node.js ), poderá usar a letinstrução com segurança .
Se você estiver escrevendo código JavaScript do lado do cliente e usar um transpiler baseado em navegador (como Traceur ou babel-standalone ), poderá usar a letinstrução com segurança , no entanto, é provável que seu código seja qualquer coisa, menos ideal em relação ao desempenho.
Se você estiver escrevendo código JavaScript do lado do cliente e usar um transpiler baseado em Nó (como o script shell de rastreamento ou Babel ), poderá usar a letinstrução com segurança . E como o seu navegador só conhece o código transpilado, as desvantagens do desempenho devem ser limitadas.
Se você estiver escrevendo um código JavaScript do lado do cliente e não usa um transpiler, considere o suporte ao navegador.
Estes são alguns navegadores que não oferecem suporte let:
Internet Explorer 10 e inferior
Firefox 43 e abaixo
Safari 9 e abaixo
Navegador Android 4 e abaixo
Opera 27 e abaixo
Chome 40 e abaixo
QUALQUER versão do Opera Mini & Blackberry Browser
Como acompanhar o suporte ao navegador
Para uma visão geral atualizada de quais navegadores suportam a letdeclaração no momento da leitura desta resposta, consulte esta Can I Usepágina .
(*) Variáveis com escopo global e funcional podem ser inicializadas e usadas antes de serem declaradas porque as variáveis JavaScript são hasteadas . Isso significa que as declarações sempre estão no topo do escopo.
"NÃO É conhecido" é enganoso, porque a variável é declarada lá devido à elevação.
Oriol
O exemplo acima é enganoso, as variáveis 'i' e 'j' não são conhecidas fora do bloco. As variáveis 'Let' têm escopo apenas nesse bloco em particular e não fora dele. Let também tem outras vantagens: você não pode redeclarar a variável novamente e ela mantém o escopo lexical.
zakir
1
Isso foi útil, obrigado! Eu acho que seria ainda mais útil ser específico sobre o que você quer dizer com "Modern JavaScript" e "Old school JavaScript"; Penso que estes correspondem ao ECMAScript 6 / ES6 / ECMAScript 2015 e a versões anteriores, respectivamente?
9118 Jon Schneider #:
1
@ JonSchneider: Correto! Onde digo "JavaScript da velha escola", estou falando sobre o ECMAScript 5 e, quando estou me referindo ao "JavaScript moderno", estou abordando o ECMAScript 6 (também conhecido como ECMAScript 2015). Porém, não achei que fosse tão importante entrar em detalhes aqui, pois a maioria das pessoas quer saber (1) qual a diferença entre o escopo do bloco e o escopo funcional, (2) quais navegadores suportam o escopo do bloco e (3) se é seguro usar o escopo do bloco hoje para qualquer projeto em que eles estejam trabalhando. Então, concentrei minha resposta em resolver esses problemas.
John Slegers
1
@ JonSchneider: (continuação) No entanto, acabei de adicionar um link para um artigo da Smashing Magazine no ES6 / ES2015 para aqueles que desejam aprender mais sobre quais recursos foram adicionados ao JavaScript nos últimos dois anos ... de qualquer pessoa que pode estar se perguntando o que quero dizer com "JavaScript moderno".
John Slegers
39
Aqui está um exemplo:
<script>var globalVariable =7;//==window.globalVariablefunction aGlobal( param ){//==window.aGlobal(); //param is only accessible in this functionvar scopedToFunction ={//can't be accessed outside of this function
nested :3//accessible by: scopedToFunction.nested};
anotherGlobal ={//global because there's no `var`};}</script>
Você deseja investigar os fechamentos e como usá-los para criar membros privados .
Sim, mas é seguro usar? Quero dizer, eu escolheria realisticamente essa implementação se meu código fosse executado no WebKit?
IgorGanapolsky
10
@ Python: Não, o WebKit não suporta let.
Kennytm
Eu acho que o único uso válido para isso seria se você soubesse que todos os clientes usariam um navegador Mozilla como para o sistema interno de uma empresa.
GazB 11/11/12
Ou se você estiver programando usando a estrutura XUL, a estrutura de interface do Mozilla onde você constrói usando css, xml e javascript.
precisa
1
@GazB mesmo que seja uma idéia horrível! Então hoje você sabe que seus clientes estão usando o Mozilla e, em seguida, sai uma nova nota informando que agora eles estão usando outra coisa. IE, a razão pela qual nosso sistema de pagamento é péssimo ... Você deve usar o IE8 e nunca o IE9 ou IE10 ou Firefox ou Chrome, porque ele
simplesmente
25
A ideia do escopo do JavaScript, quando originalmente projetada por Brendan Eich, veio da linguagem de script HyperCard HyperTalk .
Nesse idioma, as exibições eram feitas de maneira semelhante a uma pilha de cartões de índice. Havia um cartão mestre conhecido como plano de fundo. Era transparente e pode ser visto como o cartão inferior. Qualquer conteúdo deste cartão base foi compartilhado com os cartões colocados em cima dele. Cada cartão colocado em cima tinha seu próprio conteúdo, que prevalecia sobre o cartão anterior, mas ainda assim tinha acesso aos cartões anteriores, se desejado.
É exatamente assim que o sistema de escopo do JavaScript é projetado. Só tem nomes diferentes. Os cartões em JavaScript são conhecidos como Execution Contexts ECMA . Cada um desses contextos contém três partes principais. Um ambiente variável, um ambiente lexical e essa ligação. Voltando à referência de cartões, o ambiente lexical contém todo o conteúdo dos cartões anteriores, mais abaixo na pilha. O contexto atual está no topo da pilha e qualquer conteúdo declarado lá será armazenado no ambiente variável. O ambiente variável terá precedência no caso de colisões de nomes.
A ligação this apontará para o objeto que contém. Às vezes, os escopos ou os contextos de execução são alterados sem que o objeto contido seja alterado, como em uma função declarada em que o objeto contido pode estar windowou uma função construtora.
Esses contextos de execução são criados sempre que o controle é transferido. O controle é transferido quando o código começa a ser executado, e isso é feito principalmente a partir da execução da função.
Então essa é a explicação técnica. Na prática, é importante lembrar que em JavaScript
Os escopos são tecnicamente "contextos de execução"
Os contextos formam uma pilha de ambientes onde as variáveis são armazenadas
A parte superior da pilha tem precedência (a parte inferior é o contexto global)
Cada função cria um contexto de execução (mas nem sempre é uma nova ligação)
Aplicando isso a um dos exemplos anteriores (5. "Fechamento") nesta página, é possível seguir a pilha de contextos de execução. Neste exemplo, existem três contextos na pilha. Eles são definidos pelo contexto externo, o contexto na função chamada imediatamente chamada por var six e o contexto na função retornada dentro da função chamada imediatamente pela var six.
i ) O contexto externo. Possui um ambiente variável de a = 1 ii ) O contexto IIFE, possui um ambiente lexical de a = 1, mas um ambiente variável de a = 6 que tem precedência na pilha iii ) O contexto de função retornado possui um léxico ambiente de a = 6 e esse é o valor referenciado no alerta quando chamado.
1) Existe um escopo global, um escopo de função e os escopos with e catch. Em geral, não existe um escopo de nível de 'bloco' para as variáveis - as instruções with e catch adicionam nomes aos seus blocos.
2) Os escopos são aninhados por funções até o escopo global.
3) As propriedades são resolvidas passando pela cadeia de protótipos. A instrução with traz nomes de propriedades do objeto para o escopo lexical definido pelo bloco with.
EDIT: ECMAAScript 6 (Harmony) é especificado para suportar let, e eu sei que o chrome permite uma flag de 'harmonia', então talvez ele o suporte ..
Let seria um suporte para o escopo no nível do bloco, mas você precisa usar a palavra-chave para que isso aconteça.
EDIT: Com base no fato de Benjamin apontar as declarações with e catch nos comentários, editei o post e adicionei mais. As instruções with e catch introduzem variáveis em seus respectivos blocos, e esse é um escopo de bloco. Essas variáveis são alias às propriedades dos objetos passados para elas.
//chrome (v8)var a ={'test1':'test1val'}
test1 // error not definedwith(a){var test1 ='replaced'}
test1 // undefined
a // a.test1 = 'replaced'
EDIT: Exemplo esclarecedor:
O teste1 tem como escopo o bloco com, mas é alias para a.test1. 'Var test1' cria uma nova variável test1 no contexto lexical superior (função ou global), a menos que seja uma propriedade de a - como é.
Caramba! Cuidado ao usar 'with' - assim como var é um noop se a variável já estiver definida na função, também é um noop com relação aos nomes importados do objeto! Um pouco de atenção no nome já sendo definido tornaria isso muito mais seguro. Eu pessoalmente nunca vou usar por causa disso.
Você tem alguns erros aqui, pois um JavaScript tem formas de escopo de bloco.
Benjamin Gruenbaum 25/10/2013
Meus ouvidos (olhos) estão abertos, Benjamin - Minhas declarações acima são como tenho tratado o escopo do Javascript, mas elas não se baseiam na leitura das especificações. E espero que você não esteja se referindo à declaração with (que é uma forma de escopo de objeto) ou à sintaxe especial 'let' do Mozilla.
precisa
Bem, a withdeclaração é uma forma de escopo de bloco, mas as catchcláusulas são uma forma muito mais comum (fato divertido, a v8 implementa catchcom a with) - são praticamente as únicas formas de escopo de bloco no próprio JavaScript (ou seja, função, global, try / catch , com e seus derivados), no entanto, os ambientes host possuem diferentes noções de escopo - por exemplo, eventos embutidos no navegador e no módulo vm do NodeJS.
Benjamin Gruenbaum 25/10
Benjamin - pelo que posso ver, tanto com como com catch apenas introduzem o objeto no escopo atual (e, portanto, nas propriedades), mas depois que o respectivo bloco termina, as variáveis são redefinidas. Mas, por exemplo, uma nova variável introduzida em uma captura terá o escopo da função / método envolvente.
Gerard ONeill
2
Que é exatamente o meio bloco de escopo :)
Benjamin Gruenbaum
9
Descobri que muitas pessoas novas no JavaScript têm problemas para entender que a herança está disponível por padrão no idioma e que o escopo da função é o único escopo até agora. Forneci uma extensão para um embelezador que escrevi no final do ano passado chamado JSPretty. As cores do recurso funcionam com o escopo no código e sempre associam uma cor a todas as variáveis declaradas nesse escopo. O fechamento é demonstrado visualmente quando uma variável com uma cor de um escopo é usada em um escopo diferente.
Não funciona para mim no Firefox 26. Colo código ou carrego um arquivo, clique em executar e nada acontece.
Mplwork
Escopo e herança são duas coisas diferentes.
Ben Aston
9
JavaScript tem apenas dois tipos de escopo:
Escopo Global : Global nada mais é do que um escopo no nível da janela. Aqui, variável presente em todo o aplicativo.
Escopo Funcional : Variável declarada dentro de uma função comvar palavra-chave tem escopo funcional.
Sempre que uma função é chamada, um objeto de escopo variável é criado (e incluído na cadeia de escopo) que é seguido por variáveis em JavaScript.
a ="global";function outer(){
b ="local";
console.log(a+b);//"globallocal"}
outer();
Cadeia de escopo ->
Nível da janela - a e outerfunção estão no nível superior da cadeia de escopo.
quando a função externa chamou um novo variable scope object(e incluído na cadeia de escopo) adicionado com variávelb dentro dela.
Agora, quando uma variável arequerida, ele primeiro procura o escopo da variável mais próximo e, se a variável não estiver lá, passa para o próximo objeto da cadeia de escopo da variável., Que nesse caso é o nível da janela.
Não sei por que essa não é a resposta aceita. Na verdade, existe apenas escopo funcional (antes do ECMA6 não havia "escopo local") e ligações globais
texasbruce 28/02/2015
9
Apenas para adicionar às outras respostas, o escopo é uma lista de pesquisa de todos os identificadores declarados (variáveis) e aplica um conjunto estrito de regras sobre como elas são acessíveis ao código atualmente em execução. Essa pesquisa pode ser para fins de atribuição à variável, que é uma referência LHS (lado esquerdo), ou pode ser para fins de recuperação de seu valor, que é uma referência RHS (lado direito). Essas pesquisas são o que o mecanismo JavaScript está fazendo internamente quando está compilando e executando o código.
Então, sob essa perspectiva, acho que uma imagem ajudaria o que encontrei no ebook Scopes and Closures de Kyle Simpson:
Citando seu ebook:
O edifício representa o conjunto de regras de escopo aninhado do nosso programa. O primeiro andar do edifício representa seu escopo em execução no momento, onde você estiver. O nível superior do edifício é o escopo global. Você resolve as referências de LHS e RHS olhando em seu andar atual e, se não o encontrar, pegue o elevador para o próximo andar, procure lá, depois o próximo e assim por diante. Depois de chegar ao último andar (o escopo global), você encontra o que está procurando ou não. Mas você tem que parar de qualquer maneira.
Vale ressaltar que "a pesquisa de escopo para quando encontra a primeira correspondência".
Essa idéia de "níveis de escopo" explica por que "isso" pode ser alterado com um escopo recém-criado, se estiver sendo procurado em uma função aninhada. Aqui está um link que aborda todos esses detalhes. Tudo o que você queria saber sobre o escopo do javascript
Variáveis globais são exatamente como estrelas globais (Jackie Chan, Nelson Mandela). Você pode acessá-los (obter ou definir o valor), a partir de qualquer parte do seu aplicativo. Funções globais são como eventos globais (Ano Novo, Natal). Você pode executá-las (chamá-las) de qualquer parte do seu aplicativo.
//global variablevar a =2;//global functionfunction b(){
console.log(a);//access global variable}
Escopo local:
Se você está nos EUA, talvez conheça Kim Kardashian, celebridade infame (ela de alguma forma consegue fazer os tablóides). Mas pessoas fora dos EUA não a reconhecerão. Ela é uma estrela local, ligada ao seu território.
Variáveis locais são como estrelas locais. Você só pode acessá-los (obter ou definir o valor) dentro do escopo. Uma função local é como eventos locais - você pode executar apenas (comemorar) dentro desse escopo. Se você deseja acessá-los fora do escopo, receberá um erro de referência
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
Existem QUASE apenas dois tipos de escopos de JavaScript:
o escopo de cada declaração var está associado à função de fechamento mais imediata
se não houver função anexa para uma declaração var, é escopo global
Portanto, quaisquer blocos que não sejam funções não criam um novo escopo. Isso explica por que os loops de substituição substituem as variáveis com escopo externo:
var i =10, v =10;for(var i =0; i <5; i++){var v =5;}
console.log(i, v);// output 5 5
Em vez disso, usando funções:
var i =10, v =10;
$.each([0,1,2,3,4],function(i){var v =5;});
console.log(i,v);// output 10 10
No primeiro exemplo, não havia escopo de bloco, portanto, as variáveis declaradas inicialmente foram substituídas. No segundo exemplo, havia um novo escopo devido à função; portanto, as variáveis declaradas inicialmente eram SOMBRA e não substituídas.
Isso é quase tudo o que você precisa saber em termos de escopo do JavaScript, exceto:
try / catch introduz APENAS um novo escopo SOMENTE para a própria variável de exceção, outras variáveis não têm um novo escopo
Assim, você pode ver que o escopo do JavaScript é realmente extremamente simples, embora nem sempre seja intuitivo. Algumas coisas a ter em atenção:
declarações var são hasteadas no topo do escopo. Isso significa que não importa onde a declaração var acontece, para o compilador é como se a própria var acontecesse no topo
várias declarações var dentro do mesmo escopo são combinadas
Portanto, este código:
var i =1;function abc(){
i =2;var i =3;}
console.log(i);// outputs 1
é 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);
Isso pode parecer contra-intuitivo, mas faz sentido da perspectiva de um designer de linguagem imperativo.
Você deve usar o escopo do bloco para todas as variáveis criadas, assim como a maioria dos outros idiomas principais. varé obsoleto . Isso torna seu código mais seguro e mais sustentável.
constdeve ser usado em 95% dos casos . Faz com que a referência da variável não possa ser alterada. A matriz, o objeto e as propriedades do nó DOM podem mudar e provavelmente devem estar const.
letdeve ser usado para qualquer variável que espera ser reatribuída. Isso inclui um loop for. Se você alterar algum valor além da inicialização, use let.
O escopo do bloco significa que a variável estará disponível apenas dentro dos colchetes em que é declarada. Isso se estende aos escopos internos, incluindo funções anônimas criadas dentro do seu escopo.
Tente este exemplo curioso. No exemplo abaixo, se a fosse um numérico inicializado em 0, você veria 0 e, em seguida, 1. Exceto a é um objeto e o javascript passará f1 um ponteiro de um em vez de uma cópia dele. O resultado é que você recebe o mesmo alerta as duas vezes.
var a =newDate();function f1(b){
b.setDate(b.getDate()+1);
alert(b.getDate());}
f1(a);
alert(a.getDate());
Existem apenas escopos de função em JS. Não bloqueie os escopos! Você também pode ver o que está içando.
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);
Meu entendimento é que existem três escopos: escopo global, disponível globalmente; escopo local, disponível para uma função inteira, independentemente de blocos; e escopo do bloco, disponível apenas para o bloco, instrução ou expressão em que foi usado. O escopo global e local é indicado com a palavra-chave 'var', dentro de uma função ou fora, e o escopo do bloco é indicado com a palavra-chave 'let'.
Para aqueles que acreditam que há apenas escopo global e local, explique por que a Mozilla teria uma página inteira descrevendo as nuances do escopo do bloco em JS.
Um problema muito comum ainda não descrito, no qual os codificadores de front-end frequentemente se deparam é o escopo visível para um manipulador de eventos embutido no HTML - por exemplo, com
<buttononclick="foo()"></button>
O escopo das variáveis às quais um on*atributo pode fazer referência deve ser:
global (os manipuladores inline trabalhando quase sempre fazem referência a variáveis globais)
uma propriedade do documento (por exemplo, querySelectorcomo uma variável autônoma apontará para document.querySelector; raro)
uma propriedade do elemento ao qual o manipulador está anexado (como acima; raro)
Caso contrário, você obterá um ReferenceError quando o manipulador for chamado. Portanto, por exemplo, se o manipulador inline fizer referência a uma função definida dentro dewindow.onload ou $(function() {, a referência falhará, porque o manipulador inline pode fazer referência apenas a variáveis no escopo global e a função não é global:
As propriedades documente as propriedades do elemento ao qual o manipulador está anexado também podem ser referenciadas como variáveis independentes dentro de manipuladores inline, pois os manipuladores inline são chamados dentro de dois withblocos , um para o documente outro para o elemento. A cadeia de escopo de variáveis dentro desses manipuladores é extremamente pouco intuitiva , e um manipulador de eventos em funcionamento provavelmente exigirá que uma função seja global (e provavelmente a poluição global desnecessária deve ser evitada ).
Como a cadeia de escopo dentro dos manipuladores inline é muito estranha , e como os manipuladores inline exigem que a poluição global funcione, e como os manipuladores inline às vezes exigem uma fuga feia de caracteres ao passar argumentos, provavelmente é mais fácil evitá-los. Em vez disso, anexe manipuladores de eventos usando Javascript (como com addEventListener), em vez de com marcação HTML.
Em uma observação diferente, diferentemente das <script>tags normais , que são executadas no nível superior, o código dentro dos módulos ES6 é executado em seu próprio escopo privado. Uma variável definida na parte superior de uma <script>tag normal é global, portanto, você pode referenciá-la em outras <script>tags, como esta:
Mas o nível superior de um módulo ES6 não é global. Uma variável declarada na parte superior de um módulo ES6 será visível apenas dentro desse módulo, a menos que a variável seja explicitamente exporteditada ou a menos que esteja atribuída a uma propriedade do objeto global.
<scripttype="module">const foo ='foo';</script><script>// Can't access foo here, because the other script is a module
console.log(typeof foo);</script>
O nível superior de um módulo ES6 é semelhante ao do interior de um IIFE no nível superior normalmente <script>. O módulo pode fazer referência a quaisquer variáveis globais, e nada pode fazer referência a nada dentro do módulo, a menos que o módulo seja projetado explicitamente para ele.
Variáveis em Javascript foram inicialmente (pré ES6) lexicamente definidas no escopo da função. O termo com escopo lexical significa que você pode ver o escopo das variáveis 'observando' o código.
Toda variável declarada com a varpalavra-chave tem o escopo definido para a função No entanto, se outra função for declarada nessa função, essas funções terão acesso às variáveis das funções externas. Isso é chamado de cadeia de escopo . Funciona da seguinte maneira:
Quando uma função procura resolver um valor de variável, primeiro analisa seu próprio escopo. Este é o corpo da função, ou seja, tudo entre colchetes {} (exceto para variáveis dentro de outras funções que estão neste escopo).
Se não conseguir encontrar a variável dentro do corpo da função, ela subirá para a cadeia e examinará o escopo da variável na função em que a função foi definida . Isto é o que se entende com escopo lexical, podemos ver no código onde essa função foi definida e, portanto, podemos determinar a cadeia de escopo simplesmente olhando o código.
O que acontece quando estamos tentando registrar as variáveis foo, bare foobarpara o console é o seguinte:
Tentamos logar foo no console, foo pode ser encontrado dentro da innerFuncprópria função . Portanto, o valor de foo é resolvido para a sequência innerFunc.
Tentamos registrar a barra no console, a barra não pode ser encontrada dentro da innerFuncprópria função . Portanto, precisamos escalar a cadeia de escopo . Primeiro, examinamos a função externa na qual a função innerFuncfoi definida. Essa é a função outerFunc. No escopo deouterFunc , podemos encontrar a barra variável, que contém a string 'outerFunc'.
foobar não pode ser encontrado em innerFunc. . Portanto, precisamos escalar a cadeia de escopo para o escopo innerFunc. Também não pode ser encontrado aqui, subimos outro nível para o escopo global (ou seja, o escopo mais externo). Encontramos aqui a variável foobar que contém a string 'global'. Se não tivesse encontrado a variável após escalar a cadeia de escopo, o mecanismo JS lançaria um referenceError .
ES6 (ES 2015) e mais antigos:
Os mesmos conceitos de escopo e cadeia de expressão lexicamente ainda se aplicam ES6. No entanto, foram introduzidas novas maneiras de declarar variáveis. Existem os seguintes:
let: cria uma variável com escopo definido em bloco
const: cria uma variável com escopo de bloco que precisa ser inicializada e não pode ser reatribuída
A maior diferença entre vare let/ consté o varescopo da função, enquanto let/ consté o escopo do bloco. Aqui está um exemplo para ilustrar isso:
let letVar ='global';var varVar ='global';function foo (){if(true){// this variable declared with let is scoped to the if block, block scopedlet letVar =5;// this variable declared with let is scoped to the function block, function scopedvar varVar =10;}
console.log(letVar);
console.log(varVar);}
foo();
No exemplo acima, letVar registra o valor global porque as variáveis declaradas com lettêm escopo de bloco. Eles deixam de existir fora do respectivo bloco, portanto, a variável não pode ser acessada fora do bloco if.
No EcmaScript5, existem principalmente dois escopos, escopo local e escopo global, mas no EcmaScript6 temos três escopos, escopo local, escopo global e um novo escopo chamado escopo de bloco .
Exemplo de escopo de bloco é: -
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.}
O ECMAScript 6 introduziu as palavras-chave let e const. Essas palavras-chave podem ser usadas no lugar da palavra-chave var. Ao contrário da palavra-chave var, as palavras-chave let e const suportam a declaração do escopo local dentro das instruções do bloco.
var x =10let y =10const z =10{
x =20let y =20const z =20{
x =30// x is in the global scope because of the 'var' keywordlet y =30// y is in the local scope because of the 'let' keywordconst 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
Eu realmente gosto da resposta aceita, mas quero adicionar isso:
O escopo coleta e mantém uma lista de pesquisa de todos os identificadores declarados (variáveis) e aplica um conjunto estrito de regras sobre como elas são acessíveis ao código atualmente em execução.
Escopo é um conjunto de regras para procurar variáveis pelo nome do identificador.
Se uma variável não puder ser encontrada no escopo imediato, o Engine consultará o próximo escopo externo, continuando até que seja encontrado ou até que o escopo mais externo (aka, global) seja atingido.
É o conjunto de regras que determina onde e como uma variável (identificador) pode ser pesquisada. Essa pesquisa pode ser para fins de atribuição à variável, que é uma referência LHS (lado esquerdo) ou para recuperar seu valor, que é uma referência RHS (lado direito) .
As referências LHS resultam de operações de atribuição. As atribuições relacionadas ao escopo podem ocorrer com o operador = ou passando argumentos para (atribuir a) parâmetros de função.
O mecanismo JavaScript primeiro compila o código antes de executar e, ao fazer isso, divide instruções como var a = 2; em duas etapas separadas: 1º. Primeiro, var a para declarar nesse escopo. Isso é realizado no início, antes da execução do código. 2nd. Posteriormente, a = 2 para procurar a variável (referência LHS) e atribuí-la se encontrada.
As pesquisas de referência do LHS e do RHS começam no escopo em execução no momento e, se necessário (ou seja, elas não encontram o que estão procurando lá), elas avançam no escopo aninhado, um escopo (floor ) de cada vez, procurando o identificador, até que cheguem ao global (último andar) e parem, e o encontrem ou não. As referências não preenchidas do RHS resultam no lançamento do ReferenceError. As referências não preenchidas do LHS resultam em um global automático, implicitamente criado, com esse nome (se não estiver no modo estrito) ou em um ReferenceError (se estiver no modo estrito).
O escopo consiste em uma série de "bolhas" que atuam como um contêiner ou balde, no qual os identificadores (variáveis, funções) são declarados. Essas bolhas aninham-se perfeitamente umas nas outras e esse aninhamento é definido no momento do autor.
Respostas:
TLDR
O JavaScript possui escopo e fechamentos lexicais (também chamados estáticos). Isso significa que você pode dizer o escopo de um identificador olhando o código-fonte.
Os quatro escopos são:
Fora dos casos especiais do escopo global e do módulo, as variáveis são declaradas usando
var
(escopo da função),let
(escopo do bloco) econst
(escopo do bloco). A maioria das outras formas de declaração de identificador tem escopo de bloco no modo estrito.Visão geral
Escopo é a região da base de código sobre a qual um identificador é válido.
Um ambiente lexical é um mapeamento entre nomes de identificadores e os valores associados a eles.
O escopo é formado por um aninhamento vinculado de ambientes lexicais, com cada nível no aninhamento correspondendo a um ambiente lexical de um contexto de execução ancestral.
Esses ambientes lexicais vinculados formam um escopo "cadeia". A resolução do identificador é o processo de busca ao longo dessa cadeia por um identificador correspondente.
A resolução do identificador ocorre apenas em uma direção: para o exterior. Dessa maneira, os ambientes lexicais externos não podem "ver" os ambientes lexicais internos.
Existem três fatores pertinentes para decidir o escopo de um identificador em JavaScript:
Algumas das maneiras pelas quais os identificadores podem ser declarados:
var
,let
econst
var
no modo não estrito)import
afirmaçõeseval
Alguns dos identificadores de locais podem ser declarados:
Estilos de declaração
var
Identificadores declarados usando
var
têm escopo de função , além de quando são declarados diretamente no contexto global; nesse caso, eles são adicionados como propriedades no objeto global e têm escopo global. Existem regras separadas para seu uso emeval
funções.deixe e const
Identificadores declarados usando
let
econst
têm escopo de bloco , exceto quando são declarados diretamente no contexto global; nesse caso, eles têm escopo global.Nota:
let
,const
evar
estão todos içada . Isso significa que sua posição lógica de definição é a parte superior de seu escopo (bloco ou função). No entanto, as variáveis declaradas estão sendo usadaslet
econst
não podem ser lidas ou atribuídas até que o controle tenha passado o ponto de declaração no código-fonte. O período intermediário é conhecido como zona morta temporal.Nomes de parâmetros de função
Os nomes de parâmetros da função têm escopo definido para o corpo da função. Observe que há uma leve complexidade nisso. As funções declaradas como argumentos padrão fecham-se sobre a lista de parâmetros , e não o corpo da função.
Declarações de função
As declarações de função têm escopo de bloco no modo estrito e escopo de função no modo não estrito. Nota: o modo não estrito é um conjunto complicado de regras emergentes com base nas implementações históricas peculiares de diferentes navegadores.
Expressões de funções nomeadas
As expressões de funções nomeadas têm um escopo definido para elas mesmas (por exemplo, para fins de recursão).
Propriedades definidas implicitamente no objeto global
No modo não estrito, as propriedades implicitamente definidas no objeto global têm escopo global, porque o objeto global fica na parte superior da cadeia de escopo. No modo estrito, isso não é permitido.
avaliação
Nas
eval
cadeias, as variáveis declaradas usandovar
serão colocadas no escopo atual ou, seeval
usadas indiretamente, como propriedades no objeto global.Exemplos
A seguir irá lançar uma ReferenceError porque os nomes
x
,y
ez
não têm nenhum significado fora da funçãof
.A seguir, será gerado um ReferenceError para
y
ez
, mas não parax
, porque a visibilidade dex
não é restringida pelo bloco. Blocos que definem os corpos de estruturas de controle comoif
,for
ewhile
, se comportam de forma semelhante.A seguir,
x
é visível fora do loop porquevar
possui o escopo da função:... por causa desse comportamento, você precisa ter cuidado ao fechar as variáveis declaradas usando
var
loops. Há apenas uma instância da variávelx
declarada aqui e ela fica logicamente fora do loop.As seguintes impressões são impressas
5
cinco vezes e depois são impressas5
pela sexta vez pela parteconsole.log
externa do loop:A seguinte impressão é impressa
undefined
porquex
tem escopo de bloco. Os retornos de chamada são executados um por um assincronamente. Novo comportamento delet
meios variáveis que cada função anônima fechada sobre uma variável diferente chamadax
(ao contrário do que teria feito comvar
), e assim inteiros0
através4
são impressos .:O seguinte NÃO lançará a
ReferenceError
porque a visibilidade dex
não é restringida pelo bloco; no entanto, ela será impressaundefined
porque a variável não foi inicializada (devido àif
instrução).Uma variável declarada na parte superior de um
for
loop usandolet
tem o escopo definido para o corpo do loop:A seguir, será gerado um
ReferenceError
porque a visibilidade dex
é restringida pelo bloco:Variáveis declaradas usando
var
,let
ouconst
todos com escopo definido para módulos:A seguir, declararemos uma propriedade no objeto global, porque as variáveis declaradas usando
var
dentro do contexto global são adicionadas como propriedades ao objeto global:let
econst
no contexto global não adicione propriedades ao objeto global, mas ainda tenha escopo global:Os parâmetros de função podem ser considerados declarados no corpo da função:
Os parâmetros do bloco de captura têm escopo definido para o corpo do bloco de captura:
As expressões de função nomeadas têm escopo definido apenas para a própria expressão:
No modo não estrito, as propriedades definidas implicitamente no objeto global têm escopo global. No modo estrito, você recebe um erro.
No modo não estrito, as declarações de função têm escopo de função. No modo estrito, eles têm escopo de bloco.
Como funciona sob o capô
Escopo é definido como a região lexical do código sobre a qual um identificador é válido.
No JavaScript, todo objeto de função possui uma
[[Environment]]
referência oculta que é uma referência ao ambiente lexical do contexto de execução (quadro da pilha) no qual foi criado.Quando você invoca uma função, o
[[Call]]
método oculto é chamado. Este método cria um novo contexto de execução e estabelece um link entre o novo contexto de execução e o ambiente lexical do objeto de função. Isso é feito copiando o[[Environment]]
valor no objeto de função, para um campo de referência externo no ambiente lexical do novo contexto de execução.Observe que esse link entre o novo contexto de execução e o ambiente lexical do objeto de função é chamado de fechamento .
Assim, em JavaScript, o escopo é implementado através de ambientes lexicais vinculados em uma "cadeia" por referências externas. Essa cadeia de ambientes lexicais é chamada cadeia de escopo, e a resolução do identificador ocorre pesquisando-se na cadeia por um identificador correspondente.
Saiba mais .
fonte
Javascript usa cadeias de escopo para estabelecer o escopo de uma determinada função. Geralmente, há um escopo global e cada função definida possui seu próprio escopo aninhado. Qualquer função definida dentro de outra função tem um escopo local que está vinculado à função externa. É sempre a posição na fonte que define o escopo.
Um elemento na cadeia de escopo é basicamente um mapa com um ponteiro para seu escopo pai.
Ao resolver uma variável, o javascript inicia no escopo mais interno e pesquisa para fora.
fonte
Variáveis declaradas globalmente têm um escopo global. As variáveis declaradas em uma função têm escopo definido para essa função e sombream variáveis globais com o mesmo nome.
(Tenho certeza de que existem muitas sutilezas que os verdadeiros programadores JavaScript poderão apontar em outras respostas. Em particular, encontrei esta página sobre o que exatamente
this
significa a qualquer momento. Espero que este link mais introdutório seja suficiente para você começar .)fonte
JavaScript da velha escola
Tradicionalmente, o JavaScript realmente tem apenas dois tipos de escopo:
Não vou elaborar isso, pois já existem muitas outras respostas que explicam a diferença.
JavaScript moderno
As especificações JavaScript mais recentes agora também permitem um terceiro escopo:
Como crio variáveis de escopo de bloco?
Tradicionalmente, você cria suas variáveis assim:
As variáveis do escopo do bloco são criadas assim:
Então, qual é a diferença entre escopo funcional e escopo de bloco?
Para entender a diferença entre o escopo funcional e o escopo do bloco, considere o seguinte código:
Aqui, podemos ver que nossa variável
j
é conhecida apenas no primeiro loop for, mas não antes e depois. No entanto, nossa variáveli
é conhecida em toda a função.Além disso, considere que as variáveis com escopo de bloco não são conhecidas antes de serem declaradas porque não são içadas. Também não é permitido redefinir a mesma variável com escopo de bloco dentro do mesmo bloco. Isso torna as variáveis com escopo do bloco menos propensas a erros do que as variáveis com escopo global ou funcional, que são içadas e que não produzem erros no caso de várias declarações.
É seguro usar variáveis de escopo de bloco hoje?
Se é seguro usar ou não hoje, depende do seu ambiente:
Se você estiver escrevendo o código JavaScript do servidor ( Node.js ), poderá usar a
let
instrução com segurança .Se você estiver escrevendo código JavaScript do lado do cliente e usar um transpiler baseado em navegador (como Traceur ou babel-standalone ), poderá usar a
let
instrução com segurança , no entanto, é provável que seu código seja qualquer coisa, menos ideal em relação ao desempenho.Se você estiver escrevendo código JavaScript do lado do cliente e usar um transpiler baseado em Nó (como o script shell de rastreamento ou Babel ), poderá usar a
let
instrução com segurança . E como o seu navegador só conhece o código transpilado, as desvantagens do desempenho devem ser limitadas.Se você estiver escrevendo um código JavaScript do lado do cliente e não usa um transpiler, considere o suporte ao navegador.
Estes são alguns navegadores que não oferecem suporte
let
:Como acompanhar o suporte ao navegador
Para uma visão geral atualizada de quais navegadores suportam a
let
declaração no momento da leitura desta resposta, consulte estaCan I Use
página .(*) Variáveis com escopo global e funcional podem ser inicializadas e usadas antes de serem declaradas porque as variáveis JavaScript são hasteadas . Isso significa que as declarações sempre estão no topo do escopo.
fonte
Aqui está um exemplo:
Você deseja investigar os fechamentos e como usá-los para criar membros privados .
fonte
A chave, pelo que entendi, é que o Javascript tem escopo no nível de função versus o escopo do bloco C mais comum.
Aqui está um bom artigo sobre o assunto.
fonte
Em "Javascript 1.7" (extensão do Mozilla para Javascript) também é possível declarar variáveis de escopo de bloco com a
let
instrução :fonte
let
.A ideia do escopo do JavaScript, quando originalmente projetada por Brendan Eich, veio da linguagem de script HyperCard HyperTalk .
Nesse idioma, as exibições eram feitas de maneira semelhante a uma pilha de cartões de índice. Havia um cartão mestre conhecido como plano de fundo. Era transparente e pode ser visto como o cartão inferior. Qualquer conteúdo deste cartão base foi compartilhado com os cartões colocados em cima dele. Cada cartão colocado em cima tinha seu próprio conteúdo, que prevalecia sobre o cartão anterior, mas ainda assim tinha acesso aos cartões anteriores, se desejado.
É exatamente assim que o sistema de escopo do JavaScript é projetado. Só tem nomes diferentes. Os cartões em JavaScript são conhecidos como Execution Contexts ECMA . Cada um desses contextos contém três partes principais. Um ambiente variável, um ambiente lexical e essa ligação. Voltando à referência de cartões, o ambiente lexical contém todo o conteúdo dos cartões anteriores, mais abaixo na pilha. O contexto atual está no topo da pilha e qualquer conteúdo declarado lá será armazenado no ambiente variável. O ambiente variável terá precedência no caso de colisões de nomes.
A ligação this apontará para o objeto que contém. Às vezes, os escopos ou os contextos de execução são alterados sem que o objeto contido seja alterado, como em uma função declarada em que o objeto contido pode estar
window
ou uma função construtora.Esses contextos de execução são criados sempre que o controle é transferido. O controle é transferido quando o código começa a ser executado, e isso é feito principalmente a partir da execução da função.
Então essa é a explicação técnica. Na prática, é importante lembrar que em JavaScript
Aplicando isso a um dos exemplos anteriores (5. "Fechamento") nesta página, é possível seguir a pilha de contextos de execução. Neste exemplo, existem três contextos na pilha. Eles são definidos pelo contexto externo, o contexto na função chamada imediatamente chamada por var six e o contexto na função retornada dentro da função chamada imediatamente pela var six.
i ) O contexto externo. Possui um ambiente variável de a = 1
ii ) O contexto IIFE, possui um ambiente lexical de a = 1, mas um ambiente variável de a = 6 que tem precedência na pilha
iii ) O contexto de função retornado possui um léxico ambiente de a = 6 e esse é o valor referenciado no alerta quando chamado.
fonte
1) Existe um escopo global, um escopo de função e os escopos with e catch. Em geral, não existe um escopo de nível de 'bloco' para as variáveis - as instruções with e catch adicionam nomes aos seus blocos.
2) Os escopos são aninhados por funções até o escopo global.
3) As propriedades são resolvidas passando pela cadeia de protótipos. A instrução with traz nomes de propriedades do objeto para o escopo lexical definido pelo bloco with.
EDIT: ECMAAScript 6 (Harmony) é especificado para suportar let, e eu sei que o chrome permite uma flag de 'harmonia', então talvez ele o suporte ..
Let seria um suporte para o escopo no nível do bloco, mas você precisa usar a palavra-chave para que isso aconteça.
EDIT: Com base no fato de Benjamin apontar as declarações with e catch nos comentários, editei o post e adicionei mais. As instruções with e catch introduzem variáveis em seus respectivos blocos, e esse é um escopo de bloco. Essas variáveis são alias às propriedades dos objetos passados para elas.
EDIT: Exemplo esclarecedor:
O teste1 tem como escopo o bloco com, mas é alias para a.test1. 'Var test1' cria uma nova variável test1 no contexto lexical superior (função ou global), a menos que seja uma propriedade de a - como é.
Caramba! Cuidado ao usar 'with' - assim como var é um noop se a variável já estiver definida na função, também é um noop com relação aos nomes importados do objeto! Um pouco de atenção no nome já sendo definido tornaria isso muito mais seguro. Eu pessoalmente nunca vou usar por causa disso.
fonte
with
declaração é uma forma de escopo de bloco, mas ascatch
cláusulas são uma forma muito mais comum (fato divertido, a v8 implementacatch
com awith
) - são praticamente as únicas formas de escopo de bloco no próprio JavaScript (ou seja, função, global, try / catch , com e seus derivados), no entanto, os ambientes host possuem diferentes noções de escopo - por exemplo, eventos embutidos no navegador e no módulo vm do NodeJS.Descobri que muitas pessoas novas no JavaScript têm problemas para entender que a herança está disponível por padrão no idioma e que o escopo da função é o único escopo até agora. Forneci uma extensão para um embelezador que escrevi no final do ano passado chamado JSPretty. As cores do recurso funcionam com o escopo no código e sempre associam uma cor a todas as variáveis declaradas nesse escopo. O fechamento é demonstrado visualmente quando uma variável com uma cor de um escopo é usada em um escopo diferente.
Experimente o recurso em:
Veja uma demonstração em:
Veja o código em:
Atualmente, o recurso oferece suporte para uma profundidade de 16 funções aninhadas, mas atualmente não colore variáveis globais.
fonte
JavaScript tem apenas dois tipos de escopo:
var
palavra-chave tem escopo funcional.Sempre que uma função é chamada, um objeto de escopo variável é criado (e incluído na cadeia de escopo) que é seguido por variáveis em JavaScript.
Cadeia de escopo ->
a
eouter
função estão no nível superior da cadeia de escopo.variable scope object
(e incluído na cadeia de escopo) adicionado com variávelb
dentro dela.Agora, quando uma variável
a
requerida, ele primeiro procura o escopo da variável mais próximo e, se a variável não estiver lá, passa para o próximo objeto da cadeia de escopo da variável., Que nesse caso é o nível da janela.fonte
Apenas para adicionar às outras respostas, o escopo é uma lista de pesquisa de todos os identificadores declarados (variáveis) e aplica um conjunto estrito de regras sobre como elas são acessíveis ao código atualmente em execução. Essa pesquisa pode ser para fins de atribuição à variável, que é uma referência LHS (lado esquerdo), ou pode ser para fins de recuperação de seu valor, que é uma referência RHS (lado direito). Essas pesquisas são o que o mecanismo JavaScript está fazendo internamente quando está compilando e executando o código.
Então, sob essa perspectiva, acho que uma imagem ajudaria o que encontrei no ebook Scopes and Closures de Kyle Simpson:
Citando seu ebook:
Vale ressaltar que "a pesquisa de escopo para quando encontra a primeira correspondência".
Essa idéia de "níveis de escopo" explica por que "isso" pode ser alterado com um escopo recém-criado, se estiver sendo procurado em uma função aninhada. Aqui está um link que aborda todos esses detalhes. Tudo o que você queria saber sobre o escopo do javascript
fonte
execute o código. espero que isso dê uma idéia sobre o escopo
fonte
Âmbito global :
Variáveis globais são exatamente como estrelas globais (Jackie Chan, Nelson Mandela). Você pode acessá-los (obter ou definir o valor), a partir de qualquer parte do seu aplicativo. Funções globais são como eventos globais (Ano Novo, Natal). Você pode executá-las (chamá-las) de qualquer parte do seu aplicativo.
Escopo local:
Se você está nos EUA, talvez conheça Kim Kardashian, celebridade infame (ela de alguma forma consegue fazer os tablóides). Mas pessoas fora dos EUA não a reconhecerão. Ela é uma estrela local, ligada ao seu território.
Variáveis locais são como estrelas locais. Você só pode acessá-los (obter ou definir o valor) dentro do escopo. Uma função local é como eventos locais - você pode executar apenas (comemorar) dentro desse escopo. Se você deseja acessá-los fora do escopo, receberá um erro de referência
Confira este artigo para entender profundamente o escopo
fonte
Existem QUASE apenas dois tipos de escopos de JavaScript:
Portanto, quaisquer blocos que não sejam funções não criam um novo escopo. Isso explica por que os loops de substituição substituem as variáveis com escopo externo:
Em vez disso, usando funções:
No primeiro exemplo, não havia escopo de bloco, portanto, as variáveis declaradas inicialmente foram substituídas. No segundo exemplo, havia um novo escopo devido à função; portanto, as variáveis declaradas inicialmente eram SOMBRA e não substituídas.
Isso é quase tudo o que você precisa saber em termos de escopo do JavaScript, exceto:
Assim, você pode ver que o escopo do JavaScript é realmente extremamente simples, embora nem sempre seja intuitivo. Algumas coisas a ter em atenção:
Portanto, este código:
é equivalente a:
Isso pode parecer contra-intuitivo, mas faz sentido da perspectiva de um designer de linguagem imperativo.
fonte
Js modernos, ES6 +, '
const
' e 'let
'Você deve usar o escopo do bloco para todas as variáveis criadas, assim como a maioria dos outros idiomas principais.
var
é obsoleto . Isso torna seu código mais seguro e mais sustentável.const
deve ser usado em 95% dos casos . Faz com que a referência da variável não possa ser alterada. A matriz, o objeto e as propriedades do nó DOM podem mudar e provavelmente devem estarconst
.let
deve ser usado para qualquer variável que espera ser reatribuída. Isso inclui um loop for. Se você alterar algum valor além da inicialização, uselet
.O escopo do bloco significa que a variável estará disponível apenas dentro dos colchetes em que é declarada. Isso se estende aos escopos internos, incluindo funções anônimas criadas dentro do seu escopo.
fonte
Tente este exemplo curioso. No exemplo abaixo, se a fosse um numérico inicializado em 0, você veria 0 e, em seguida, 1. Exceto a é um objeto e o javascript passará f1 um ponteiro de um em vez de uma cópia dele. O resultado é que você recebe o mesmo alerta as duas vezes.
fonte
Existem apenas escopos de função em JS. Não bloqueie os escopos! Você também pode ver o que está içando.
fonte
Meu entendimento é que existem três escopos: escopo global, disponível globalmente; escopo local, disponível para uma função inteira, independentemente de blocos; e escopo do bloco, disponível apenas para o bloco, instrução ou expressão em que foi usado. O escopo global e local é indicado com a palavra-chave 'var', dentro de uma função ou fora, e o escopo do bloco é indicado com a palavra-chave 'let'.
Para aqueles que acreditam que há apenas escopo global e local, explique por que a Mozilla teria uma página inteira descrevendo as nuances do escopo do bloco em JS.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
fonte
Um problema muito comum ainda não descrito, no qual os codificadores de front-end frequentemente se deparam é o escopo visível para um manipulador de eventos embutido no HTML - por exemplo, com
O escopo das variáveis às quais um
on*
atributo pode fazer referência deve ser:querySelector
como uma variável autônoma apontará paradocument.querySelector
; raro)Caso contrário, você obterá um ReferenceError quando o manipulador for chamado. Portanto, por exemplo, se o manipulador inline fizer referência a uma função definida dentro de
window.onload
ou$(function() {
, a referência falhará, porque o manipulador inline pode fazer referência apenas a variáveis no escopo global e a função não é global:Mostrar snippet de código
As propriedades
document
e as propriedades do elemento ao qual o manipulador está anexado também podem ser referenciadas como variáveis independentes dentro de manipuladores inline, pois os manipuladores inline são chamados dentro de doiswith
blocos , um para odocument
e outro para o elemento. A cadeia de escopo de variáveis dentro desses manipuladores é extremamente pouco intuitiva , e um manipulador de eventos em funcionamento provavelmente exigirá que uma função seja global (e provavelmente a poluição global desnecessária deve ser evitada ).Como a cadeia de escopo dentro dos manipuladores inline é muito estranha , e como os manipuladores inline exigem que a poluição global funcione, e como os manipuladores inline às vezes exigem uma fuga feia de caracteres ao passar argumentos, provavelmente é mais fácil evitá-los. Em vez disso, anexe manipuladores de eventos usando Javascript (como com
addEventListener
), em vez de com marcação HTML.Mostrar snippet de código
Em uma observação diferente, diferentemente das
<script>
tags normais , que são executadas no nível superior, o código dentro dos módulos ES6 é executado em seu próprio escopo privado. Uma variável definida na parte superior de uma<script>
tag normal é global, portanto, você pode referenciá-la em outras<script>
tags, como esta:Mostrar snippet de código
Mas o nível superior de um módulo ES6 não é global. Uma variável declarada na parte superior de um módulo ES6 será visível apenas dentro desse módulo, a menos que a variável seja explicitamente
export
editada ou a menos que esteja atribuída a uma propriedade do objeto global.Mostrar snippet de código
O nível superior de um módulo ES6 é semelhante ao do interior de um IIFE no nível superior normalmente
<script>
. O módulo pode fazer referência a quaisquer variáveis globais, e nada pode fazer referência a nada dentro do módulo, a menos que o módulo seja projetado explicitamente para ele.fonte
No JavaScript, existem dois tipos de escopo:
A função Abaixo tem uma variável de escopo local
carName
. E essa variável não é acessível de fora da função.A classe Abaixo tem uma variável de escopo Global
carName
. E essa variável é acessível de qualquer lugar da classe.fonte
ES5
e anterior:Variáveis em Javascript foram inicialmente (pré
ES6
) lexicamente definidas no escopo da função. O termo com escopo lexical significa que você pode ver o escopo das variáveis 'observando' o código.Toda variável declarada com a
var
palavra-chave tem o escopo definido para a função No entanto, se outra função for declarada nessa função, essas funções terão acesso às variáveis das funções externas. Isso é chamado de cadeia de escopo . Funciona da seguinte maneira:Exemplo:
O que acontece quando estamos tentando registrar as variáveis
foo
,bar
efoobar
para o console é o seguinte:innerFunc
própria função . Portanto, o valor de foo é resolvido para a sequênciainnerFunc
.innerFunc
própria função . Portanto, precisamos escalar a cadeia de escopo . Primeiro, examinamos a função externa na qual a funçãoinnerFunc
foi definida. Essa é a funçãoouterFunc
. No escopo deouterFunc
, podemos encontrar a barra variável, que contém a string 'outerFunc'.ES6
(ES 2015) e mais antigos:Os mesmos conceitos de escopo e cadeia de expressão lexicamente ainda se aplicam
ES6
. No entanto, foram introduzidas novas maneiras de declarar variáveis. Existem os seguintes:let
: cria uma variável com escopo definido em blococonst
: cria uma variável com escopo de bloco que precisa ser inicializada e não pode ser reatribuídaA maior diferença entre
var
elet
/const
é ovar
escopo da função, enquantolet
/const
é o escopo do bloco. Aqui está um exemplo para ilustrar isso:No exemplo acima, letVar registra o valor global porque as variáveis declaradas com
let
têm escopo de bloco. Eles deixam de existir fora do respectivo bloco, portanto, a variável não pode ser acessada fora do bloco if.fonte
No EcmaScript5, existem principalmente dois escopos, escopo local e escopo global, mas no EcmaScript6 temos três escopos, escopo local, escopo global e um novo escopo chamado escopo de bloco .
Exemplo de escopo de bloco é: -
fonte
O ECMAScript 6 introduziu as palavras-chave let e const. Essas palavras-chave podem ser usadas no lugar da palavra-chave var. Ao contrário da palavra-chave var, as palavras-chave let e const suportam a declaração do escopo local dentro das instruções do bloco.
fonte
Eu realmente gosto da resposta aceita, mas quero adicionar isso:
O escopo coleta e mantém uma lista de pesquisa de todos os identificadores declarados (variáveis) e aplica um conjunto estrito de regras sobre como elas são acessíveis ao código atualmente em execução.
Escopo é um conjunto de regras para procurar variáveis pelo nome do identificador.
fonte
Existem dois tipos de escopos no JavaScript.
Escopo global : a variável anunciada no escopo global pode ser usada em qualquer lugar do programa de maneira muito suave. Por exemplo:
Escopo funcional ou Escopo local : a variável declarada neste escopo pode ser usada apenas em sua própria função. Por exemplo:
fonte