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?
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?
Respostas:
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) e const
(escopo do bloco). A maioria das outras formas de declaração de identificador tem escopo de bloco no modo estrito.
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:
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 em eval
funções.
Identificadores declarados usando let
e const
têm escopo de bloco , exceto quando são declarados diretamente no contexto global; nesse caso, eles têm escopo global.
Nota: let
, const
e var
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 usadas let
e const
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.
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.
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.
As expressões de funções nomeadas têm um escopo definido para elas mesmas (por exemplo, para fins de recursão).
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.
Nas eval
cadeias, as variáveis declaradas usando var
serão colocadas no escopo atual ou, se eval
usadas indiretamente, como propriedades no objeto global.
A seguir irá lançar uma ReferenceError porque os nomes x
, y
e z
não têm nenhum significado fora da função f
.
function f() {
var x = 1
let y = 1
const 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 y
e z
, mas não para x
, porque a visibilidade de x
não é restringida pelo bloco. Blocos que definem os corpos de estruturas de controle como if
, for
e while
, se comportam de forma semelhante.
{
var x = 1
let y = 1
const 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
A seguir, x
é visível fora do loop porque var
possui o escopo da função:
for(var x = 0; x < 5; ++x) {}
console.log(x) // 5 (note this is outside the loop!)
... por causa desse comportamento, você precisa ter cuidado ao fechar as variáveis declaradas usando var
loops. Há apenas uma instância da variável x
declarada aqui e ela fica logicamente fora do loop.
As seguintes impressões são impressas 5
cinco vezes e depois são impressas 5
pela sexta vez pela parte console.log
externa 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 undefined
porque x
tem escopo de bloco. Os retornos de chamada são executados um por um assincronamente. Novo comportamento de let
meios 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 0
através 4
sã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 ReferenceError
porque a visibilidade de x
não é restringida pelo bloco; no entanto, ela será impressa undefined
porque a variável não foi inicializada (devido à if
instrução).
if(false) {
var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised
Uma variável declarada na parte superior de um for
loop usando let
tem o escopo definido para o corpo do loop:
for(let x = 0; x < 10; ++x) {}
console.log(typeof x) // undefined, because `x` is block-scoped
A seguir, será gerado um ReferenceError
porque a visibilidade de x
é restringida pelo bloco:
if(false) {
let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped
Variáveis declaradas usando var
, let
ou const
todos com escopo definido para módulos:
// module1.js
var x = 0
export function f() {}
//module2.js
import f from 'module1.js'
console.log(x) // throws ReferenceError
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:
var x = 1
console.log(window.hasOwnProperty('x')) // true
let
e const
no contexto global não adicione propriedades ao objeto global, mas ainda tenha escopo global:
let x = 1
console.log(window.hasOwnProperty('x')) // false
Os parâmetros de função podem ser considerados declarados no corpo da função:
function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function
Os parâmetros do bloco de captura têm escopo definido para o corpo do bloco de captura:
try {} catch(e) {}
console.log(typeof e) // undefined, because `e` is scoped to the catch block
As expressões de função nomeadas têm escopo definido apenas para a própria expressão:
(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression
No modo não estrito, as propriedades definidas implicitamente no objeto global têm escopo global. No modo estrito, você recebe um erro.
x = 1 // implicitly defined property on the global object (no "var"!)
console.log(x) // 1
console.log(window.hasOwnProperty('x')) // true
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.
'use strict'
{
function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped
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 .
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.
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 .)
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.
As especificações JavaScript mais recentes agora também permitem um terceiro escopo:
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";
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 here
function loop(arr) {
// i IS known here, but undefined
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( var i = 0; i < arr.length; i++ ) {
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( let j = 0; j < arr.length; j++ ) {
// i IS known here, and has a value
// j IS known here, and has a value
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
}
loop([1,2,3,4]);
for( var k = 0; k < arr.length; k++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
};
for( let l = 0; l < arr.length; l++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS known here, and has a value
};
loop([1,2,3,4]);
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
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.
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
:
Para uma visão geral atualizada de quais navegadores suportam a let
declaração no momento da leitura desta resposta, consulte esta Can 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.
Aqui está um exemplo:
<script>
var globalVariable = 7; //==window.globalVariable
function aGlobal( param ) { //==window.aGlobal();
//param is only accessible in this function
var scopedToFunction = {
//can't be accessed outside of this function
nested : 3 //accessible by: scopedToFunction.nested
};
anotherGlobal = {
//global because there's no `var`
};
}
</script>
Você deseja investigar os fechamentos e como usá-los para criar membros privados .
A chave, pelo que entendi, é que o Javascript tem escopo no nível de função versus o escopo do bloco C mais comum.
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 :
var a = 4;
let (a = 3) {
alert(a); // 3
}
alert(a); // 4
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.
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 defined
with (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.
with
declaração é uma forma de escopo de bloco, mas as catch
cláusulas são uma forma muito mais comum (fato divertido, a v8 implementa catch
com 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.
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.
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.
a = "global";
function outer(){
b = "local";
console.log(a+b); //"globallocal"
}
outer();
Cadeia de escopo ->
a
e outer
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.
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
execute o código. espero que isso dê uma idéia sobre o escopo
Name = 'global data';
document.Name = 'current document data';
(function(window,document){
var Name = 'local data';
var myObj = {
Name: 'object data',
f: function(){
alert(this.Name);
}
};
myObj.newFun = function(){
alert(this.Name);
}
function testFun(){
alert("Window Scope : " + window.Name +
"\nLocal Scope : " + Name +
"\nObject Scope : " + this.Name +
"\nCurrent document Scope : " + document.Name
);
}
testFun.call(myObj);
})(window,document);
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 variable
var a = 2;
//global function
function b(){
console.log(a); //access global variable
}
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:
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:
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:
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.
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 estar const
.
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, 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 = new Date();
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.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
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
<button onclick="foo()"></button>
O escopo das variáveis às quais um on*
atributo pode fazer referência deve ser:
querySelector
como uma variável autônoma apontará para document.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:
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 dois with
blocos , um para o document
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.
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 export
editada ou a menos que esteja atribuída a uma propriedade do objeto global.
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.
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.
function myFunction() {
var carName = "Volvo";
alert(carName);
// code here can use carName
}
A classe Abaixo tem uma variável de escopo Global carName
. E essa variável é acessível de qualquer lugar da classe.
class {
var carName = " Volvo";
// code here can use carName
function myFunction() {
alert(carName);
// code here can use carName
}
}
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:
// global scope
var foo = 'global';
var bar = 'global';
var foobar = 'global';
function outerFunc () {
// outerFunc scope
var foo = 'outerFunc';
var foobar = 'outerFunc';
innerFunc();
function innerFunc(){
// innerFunc scope
var foo = 'innerFunc';
console.log(foo);
console.log(bar);
console.log(foobar);
}
}
outerFunc();
O que acontece quando estamos tentando registrar as variáveis foo
, bar
e foobar
para o console é o seguinte:
innerFunc
própria função . Portanto, o valor de foo é resolvido para a sequência innerFunc
.innerFunc
própria função . Portanto, precisamos escalar a cadeia de escopo . Primeiro, examinamos a função externa na qual a função innerFunc
foi definida. Essa é a função outerFunc
. 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
e let
/ const
é o var
escopo 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 scoped
let letVar = 5;
// this variable declared with let is scoped to the function block, function scoped
var varVar = 10;
}
console.log(letVar);
console.log(varVar);
}
foo();
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.
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 = 10
let y = 10
const z = 10
{
x = 20
let y = 20
const z = 20
{
x = 30
// x is in the global scope because of the 'var' keyword
let y = 30
// y is in the local scope because of the 'let' keyword
const 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.
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:
var carName = " BMW";
// code here can use carName
function myFunction() {
// code here can use carName
}
Escopo funcional ou Escopo local : a variável declarada neste escopo pode ser usada apenas em sua própria função. Por exemplo:
// code here can not use carName
function myFunction() {
var carName = "BMW";
// code here can use carName
}