O que é uma breve introdução ao escopo lexical?
javascript
scoping
lexical-scope
Subba Rao
fonte
fonte
Respostas:
Eu os entendo através de exemplos. :)
Primeiro, escopo lexical (também chamado de escopo estático ), na sintaxe do tipo C:
Todo nível interno pode acessar seus níveis externos.
Existe uma outra maneira, chamada de escopo dinâmico usado pela primeira implementação do Lisp , novamente em uma sintaxe do tipo C:
Aqui você
fun
pode acessarx
emdummy1
oudummy2
, ou qualquerx
em qualquer função que chamarfun
comx
declarada nele.imprimirá 5,
imprimirá 10.
O primeiro é chamado estático porque pode ser deduzido em tempo de compilação, e o segundo é chamado dinâmico porque o escopo externo é dinâmico e depende da chamada em cadeia das funções.
Acho o escopo estático mais fácil para os olhos. A maioria dos idiomas acabou por esse caminho, até o Lisp (pode fazer as duas coisas, certo?). O escopo dinâmico é como passar referências de todas as variáveis para a função chamada.
Como um exemplo de por que o compilador não pode deduzir o escopo dinâmico externo de uma função, considere nosso último exemplo. Se escrevermos algo como isto:
A cadeia de chamadas depende de uma condição de tempo de execução. Se for verdade, a cadeia de chamadas se parece com:
Se a condição for falsa:
O escopo externo de
fun
ambos os casos é o chamador mais o chamador do chamador e assim por diante .Apenas para mencionar que a linguagem C não permite funções aninhadas nem escopo dinâmico.
fonte
JavaScript
. Portanto, acho que isso não deve ser marcado como a resposta aceita. Âmbito lexical especificamente em JS é diferentefor
loop é o problema típico. O escopo lexical do JavaScript é apenas no nível da função, a menos que o ES6let
ouconst
seja usado.Vamos tentar a definição mais curta possível:
O escopo léxico define como os nomes de variáveis são resolvidos em funções aninhadas: as funções internas contêm o escopo das funções pai, mesmo que a função pai tenha retornado .
Isso é tudo o que existe!
fonte
O código acima retornará "Eu sou apenas um local". Não retornará "Eu sou global". Como a função func () conta onde está originalmente definido, está no escopo da função whatismyscope.
Ele não se incomoda com o que está sendo chamado (o escopo global / mesmo dentro de outra função), é por isso que o valor do escopo global Eu sou global não será impresso.
Isso é chamado de escopo lexical, em que " funções são executadas usando a cadeia de escopo que estava em vigor quando foram definidas " - de acordo com o JavaScript Definition Guide.
O escopo léxico é um conceito muito, muito poderoso.
Espero que isto ajude..:)
fonte
O escopo léxico (AKA estático) refere-se à determinação do escopo de uma variável com base exclusivamente em sua posição no corpus de código textual. Uma variável sempre se refere ao seu ambiente de nível superior. É bom entendê-lo em relação ao escopo dinâmico.
fonte
O escopo define a área em que funções, variáveis e outras estão disponíveis. A disponibilidade de uma variável, por exemplo, é definida dentro do seu contexto, digamos que a função, arquivo ou objeto em que estão definidos. Geralmente chamamos essas variáveis locais.
A parte lexical significa que você pode derivar o escopo da leitura do código fonte.
O escopo léxico também é conhecido como escopo estático.
O escopo dinâmico define variáveis globais que podem ser chamadas ou referenciadas de qualquer lugar após serem definidas. Às vezes, eles são chamados de variáveis globais, embora as variáveis globais na maioria das linguagens de programação sejam de escopo lexical. Isso significa que pode ser derivado da leitura do código que a variável está disponível neste contexto. Talvez seja necessário seguir uma cláusula de usos ou inclusões para encontrar a instância ou definição, mas o código / compilador conhece a variável nesse local.
No escopo dinâmico, por outro lado, você pesquisa primeiro a função local, depois a função que chamou a função local, depois a função que chamou essa função e assim por diante, na pilha de chamadas. "Dinâmico" refere-se à mudança, pois a pilha de chamadas pode ser diferente toda vez que uma determinada função é chamada e, portanto, a função pode atingir variáveis diferentes, dependendo de onde é chamada. (veja aqui )
Para ver um exemplo interessante de escopo dinâmico, consulte aqui .
Para mais detalhes, veja aqui e aqui .
Alguns exemplos em Delphi / Object Pascal
Delphi tem escopo lexical.
O Delphi mais próximo do escopo dinâmico é o par de funções RegisterClass () / GetClass (). Para seu uso veja aqui .
Digamos que a hora em que RegisterClass ([TmyClass]) é chamada para registrar uma determinada classe não possa ser prevista lendo o código (é chamado em um método de clique no botão chamado pelo usuário), o código que chama GetClass ('TmyClass') será resultado ou não. A chamada para RegisterClass () não precisa estar no escopo lexical da unidade usando GetClass ();
Outra possibilidade de escopo dinâmico são os métodos anônimos (encerramentos) no Delphi 2009, pois eles conhecem as variáveis de sua função de chamada. Ele não segue o caminho de chamada de lá recursivamente e, portanto, não é totalmente dinâmico.
fonte
Adoro as respostas totalmente independentes e independentes do idioma de pessoas como @Arak. Como essa pergunta foi marcada como JavaScript , gostaria de incluir algumas notas muito específicas para esse idioma.
Em JavaScript, nossas opções de escopo são:
var _this = this; function callback(){ console.log(_this); }
callback.bind(this)
Vale a pena notar, eu acho, que o JavaScript realmente não tem escopo dinâmico .
.bind
ajusta athis
palavra - chave e isso é próximo, mas tecnicamente não é o mesmo.Aqui está um exemplo demonstrando as duas abordagens. Você faz isso toda vez que toma uma decisão sobre o escopo de retornos de chamada, para que isso se aplique a promessas, manipuladores de eventos e muito mais.
Lexical
Aqui está o que você pode chamar
Lexical Scoping
de retorno de chamada em JavaScript:Limite
Outra maneira de escopo é usar
Function.prototype.bind
:Esses métodos são, até onde eu sei, comportamentalmente equivalentes.
fonte
bind
não afeta o escopo.Escopo léxico: as variáveis declaradas fora de uma função são variáveis globais e são visíveis em todos os lugares em um programa JavaScript. Variáveis declaradas dentro de uma função têm escopo de função e são visíveis apenas para o código que aparece dentro dessa função.
fonte
A IBM define como:
Exemplo 1:
Exemplo 2:
fonte
O escopo léxico significa que, em um grupo aninhado de funções, as funções internas têm acesso às variáveis e outros recursos do escopo pai . Isso significa que as funções filho estão lexicamente ligadas ao contexto de execução de seus pais. Às vezes, o escopo léxico também é chamado de escopo estático .
O que você notará sobre o escopo lexical é que ele funciona adiante, significando que o nome pode ser acessado pelos contextos de execução de seus filhos. Mas não funciona de volta para os pais, o que significa que a variável
likes
não pode ser acessada pelos pais.Isso também nos diz que variáveis com o mesmo nome em diferentes contextos de execução ganham precedência de cima para baixo da pilha de execução. Uma variável, com um nome semelhante a outra variável, na função mais interna (contexto mais alto da pilha de execução) terá maior precedência.
Observe que isso é retirado daqui .
fonte
Em linguagem simples, o escopo lexical é uma variável definida fora do seu escopo ou o escopo superior está automaticamente disponível dentro do seu escopo, o que significa que você não precisa passá-lo para lá.
Exemplo:
// Saída: JavaScript
fonte
bind
. Com eles, obind
não é mais necessário. Para obter mais informações sobre essa alteração, verifique stackoverflow.com/a/34361380/11127383Falta uma parte importante da conversa em torno do escopo lexical e dinâmico : uma explicação clara do tempo de vida da variável com escopo - ou quando a variável pode ser acessada.
O escopo dinâmico corresponde apenas muito pouco ao escopo "global" da maneira como tradicionalmente pensamos (a razão pela qual eu trago a comparação entre os dois é que ele já foi mencionado - e não gosto particularmente da explicação do artigo vinculado ); provavelmente é melhor não fazermos a comparação entre global e dinâmica - embora supostamente, de acordo com o artigo vinculado, "... [seja] útil como um substituto para variáveis de escopo global".
Então, em inglês simples, qual é a distinção importante entre os dois mecanismos de escopo?
O escopo léxico foi definido muito bem nas respostas acima: variáveis de escopo lexicamente estão disponíveis - ou acessíveis - no nível local da função em que foram definidas.
No entanto - como não é o foco do OP - o escopo dinâmico não recebeu muita atenção e a atenção que recebeu significa que provavelmente precisa de um pouco mais (isso não é uma crítica a outras respostas, mas sim um "oh, essa resposta nos fez desejar que houvesse um pouco mais "). Então, aqui está um pouco mais:
O escopo dinâmico significa que uma variável é acessível ao programa maior durante a vida útil da chamada de função - ou enquanto a função está em execução. Realmente, a Wikipedia realmente faz um bom trabalho com a explicação da diferença entre os dois. Para não ofuscá-lo, eis o texto que descreve o escopo dinâmico:
fonte
O escopo léxico significa que uma função procura variáveis no contexto em que foi definida, e não no escopo imediatamente ao seu redor.
Veja como o escopo lexical funciona no Lisp, se você quiser mais detalhes. A resposta selecionada por Kyle Cronin em variáveis dinâmicas e lexicais no Common Lisp é muito mais clara do que as respostas aqui.
Coincidentemente, eu só aprendi sobre isso em uma classe Lisp, e isso também se aplica ao JavaScript.
Eu executei esse código no console do Chrome.
Resultado:
fonte
Um escopo lexical em JavaScript significa que uma variável definida fora de uma função pode ser acessada dentro de outra função definida após a declaração da variável. Mas o contrário não é verdadeiro; as variáveis definidas dentro de uma função não estarão acessíveis fora dessa função.
Esse conceito é muito usado em fechamentos em JavaScript.
Digamos que temos o código abaixo.
Agora, quando você chama add () -> isso imprimirá 3.
Portanto, a função add () está acessando a variável global
x
que é definida antes do método function add. Isso é chamado devido ao escopo lexical no JavaScript.fonte
add()
função fosse chamada imediatamente após o trecho de código fornecido, ela também imprimiria 3. O escopo lexical não significa simplesmente que uma função pode acessar variáveis globais fora do contexto local. Portanto, o código de exemplo realmente não ajuda a mostrar o significado do escopo lexical. Mostrar o escopo lexical no código realmente precisa de um contra-exemplo ou pelo menos uma explicação de outras possíveis interpretações do código.O escopo lexical refere-se ao léxico dos identificadores (por exemplo, variáveis, funções, etc.) visíveis a partir da posição atual na pilha de execução.
foo
ebar
estão sempre dentro do léxico dos identificadores disponíveis porque são globais.Quando
function1
é executado, ele tem acesso a um léxico defoo2
,bar2
,foo
, ebar
.Quando
function2
é executado, ele tem acesso a um léxico defoo3
,bar3
,foo2
,bar2
,foo
, ebar
.A razão pela qual funções globais e / ou externas não têm acesso a identificadores de funções internas é porque a execução dessa função ainda não ocorreu e, portanto, nenhum de seus identificadores foi alocado para a memória. Além disso, uma vez que o contexto interno é executado, ele é removido da pilha de execução, o que significa que todos os seus identificadores foram coletados como lixo e não estão mais disponíveis.
Finalmente, é por isso que um contexto de execução aninhado SEMPRE pode acessar seu contexto de execução de ancestrais e, portanto, por que ele tem acesso a um léxico maior de identificadores.
Vejo:
Agradecimentos especiais a @ robr3rd pela ajuda na simplificação da definição acima.
fonte
Aqui está um ângulo diferente sobre essa questão que podemos obter dando um passo atrás e analisando o papel do escopo na estrutura maior de interpretação (executando um programa). Em outras palavras, imagine que você estava construindo um intérprete (ou compilador) para uma linguagem e foi responsável por calcular a saída, dado um programa e alguma entrada para ele.
Interpretação envolve acompanhar três coisas:
Estado - ou seja, variáveis e locais de memória referenciados na pilha e na pilha.
Operações nesse estado - ou seja, todas as linhas de código do seu programa
O ambiente em que uma determinada operação é executada - a saber, a projeção do estado em uma operação.
Um intérprete inicia na primeira linha de código de um programa, calcula seu ambiente, executa a linha nesse ambiente e captura seu efeito no estado do programa. Em seguida, segue o fluxo de controle do programa para executar a próxima linha de código e repete o processo até o término do programa.
A maneira como você calcula o ambiente para qualquer operação é através de um conjunto formal de regras definidas pela linguagem de programação. O termo "ligação" é frequentemente usado para descrever o mapeamento do estado geral do programa para um valor no ambiente. Observe que por "estado geral" não queremos dizer estado global, mas sim a soma total de todas as definições alcançáveis, em qualquer ponto da execução).
Essa é a estrutura na qual o problema de escopo é definido. Agora, para a próxima parte do que são nossas opções.
Esta é a essência do escopo dinâmico , em que o ambiente em que qualquer código é executado é vinculado ao estado do programa, conforme definido pelo seu contexto de execução.
Em outras palavras, com escopo lexical, o ambiente que qualquer código vê é vinculado ao estado associado a um escopo definido explicitamente na linguagem, como um bloco ou uma função.
fonte
Pergunta antiga, mas aqui está minha opinião.
O escopo lexical (estático) refere-se ao escopo de uma variável no código-fonte .
Em uma linguagem como JavaScript, onde as funções podem ser passadas, anexadas e reconectadas a objetos diversos, você pode pensar que esse escopo depende de quem está chamando a função no momento, mas não. Alterar o escopo dessa maneira seria um escopo dinâmico, e o JavaScript não faz isso, exceto possivelmente com o
this
referência objeto.Para ilustrar o ponto:
No exemplo, a variável
a
é definida globalmente, mas sombreada nadoit()
função. Esta função retorna outra função que, como você vê, depende doa
variável fora de seu próprio escopo.Se você executar isso, descobrirá que o valor usado é
aardvark
, não oapple
qual, embora esteja no escopo dotest()
função, não está no escopo lexical da função original. Ou seja, o escopo usado é o escopo que aparece no código-fonte, não o escopo em que a função é realmente usada.Esse fato pode ter consequências irritantes. Por exemplo, você pode decidir que é mais fácil organizar suas funções separadamente e usá-las quando chegar a hora, como em um manipulador de eventos:
Este exemplo de código faz um de cada. Você pode ver que, devido ao escopo lexical, o botão
A
usa a variável interna, enquanto o botãoB
não. Você pode acabar aninhando funções mais do que gostaria.A propósito, nos dois exemplos, você também observará que as variáveis internas com escopo lexicamente persistem, mesmo que a função da função que contém tenha executado seu curso. Isso é chamado de fechamento e refere-se ao acesso de uma função aninhada a variáveis externas, mesmo que a função externa tenha sido concluída. O JavaScript precisa ser inteligente o suficiente para determinar se essas variáveis não são mais necessárias e, se não, podem ser coletadas pelo lixo.
fonte
Normalmente aprendo pelo exemplo, e aqui está uma coisinha:
fonte
Este tópico está fortemente relacionado à
bind
função interna e introduzido nas funções de seta do ECMAScript 6 . Foi realmente irritante, porque para cada novo método de "classe" (função realmente) que queríamos usar, precisávamosbind
disso para ter acesso ao escopo.O JavaScript por padrão não definir seu escopo de
this
em funções (que não define o contexto emthis
). Por padrão, você precisa dizer explicitamente qual contexto deseja ter.As funções de seta obtêm automaticamente o chamado escopo lexical (têm acesso à definição da variável em seu bloco contendo). Ao usar as funções de seta, ele se liga automaticamente
this
ao local em que a função de seta foi definida em primeiro lugar, e o contexto dessas funções de seta é seu bloco contendo.Veja como ele funciona na prática nos exemplos mais simples abaixo.
Antes das funções de seta (sem escopo lexical por padrão):
Com funções de seta (escopo lexical por padrão):
fonte