SEO, Marketing, Webdesign, Internet, Hosting, servidores

aScript

Escopo Lexical em JavaScript

O escopo léxico é um tópico que assusta muitos programadores. Uma das melhores explicações do escopo léxico pode ser encontrada no livro de Kyle Simpson, You Don’t Know JS: Scope and Closures. No entanto, até mesmo sua explicação está faltando porque ele não usa um exemplo real.

Um dos melhores exemplos reais de como o escopo léxico funciona, e por que é importante, pode ser encontrado no famoso livro didático “A Estrutura e a Interpretação de Programas de Computador” (SICP), de Harold Abelson e Gerald Jay Sussman. Aqui está um link para uma versão em PDF do livro: SICP. O SICP usa o Scheme, um dialeto do Lisp, e é considerado um dos melhores textos introdutórios da ciência da computação já escritos. Neste artigo, gostaria de revisar seu exemplo de escopo léxico usando JavaScript como linguagem de programação.

O exemplo usado por Abelson e Sussman é calcular raízes quadradas usando o método de Newton. O método de Newton funciona determinando sucessivas aproximações para a raiz quadrada de um número até que a aproximação fique dentro de um limite de tolerância para ser aceitável. Vamos trabalhar com um exemplo, como Abelson e Sussman fazem no SICP.

O exemplo que eles usam é encontrar a raiz quadrada de 2. Você começa fazendo uma suposição na raiz quadrada de 2, digamos 1. Você melhora esse palpite dividindo o número original pelo palpite e, em seguida, calculando a média desse quociente e do palpite atual para venha com o próximo palpite. Você para quando atinge um nível aceitável de aproximação. Abelson e Sussman usam o valor 0,001. Aqui está um resumo dos primeiros passos no cálculo:

Raiz quadrada a encontrar: 2
Primeiro palpite: 1
Quociente: 2/1 = 2
Média: (2 + 1) / 2 = 1,5
Próximo palpite: 1.5
Quociente: 1,5 / 2 = 1,3333
Média: (1,3333 + 1,5) / 2 = 1,4167
Próxima estimativa: 1.4167
Quociente: 1,4167 / 2 = 1,4118
Média: (1,4167 + 1,4118) / 2 = 1,4142
E assim por diante até que o palpite venha dentro do nosso limite de aproximação, que para este algoritmo é 0,001.

Uma função JavaScript para o método de Newton
Após esta demonstração do método, os autores descrevem um procedimento geral para resolver este problema no Esquema. Em vez de mostrar o código do esquema, vou escrevê-lo em JavaScript:

função sqrt_iter (guess, x) {
if (isGoodEnough (acho, x)) {
devolver palpite;
}
outro {
return sqrt_iter (melhorar (suposição, x), x);
}
}
Em seguida, precisamos detalhar várias outras funções, incluindo isGoodEnough () e improve (), juntamente com algumas outras funções auxiliares. Começaremos com improve (). Aqui está a definição:

função melhorar (acho, x) {
média de retorno (palpite, (x / palpite));
}
Essa função usa uma função auxiliar média (). Aqui está essa definição:

função média (x, y) {
retorno (x + y) / 2;
}
Agora estamos prontos para definir a função isGoodEnough (). Essa função serve para determinar quando nosso palpite está próximo o suficiente da nossa tolerância de aproximação (0,001). Aqui está a definição de isGoodEnough ():

função isGoodEnough (guess, x) {
return (Math.abs (square (guess) – x)) <0,001;
}
Esta função usa uma função square (), que é fácil de definir:

quadrado da função (x) {
return x * x;
}
Agora tudo o que precisamos é de uma função para começar as coisas:

função sqrt (x) {
return sqrt_iter (1.0, x);
}
Esta função usa 1.0 como um palpite inicial, que geralmente é bom.

Agora estamos prontos para testar nossas funções para ver se funcionam. Nós os carregamos em um shell JS e, em seguida, calculamos algumas raízes quadradas:

> .load sqrt_iter.js
> sqrt (3)
1,7321428571428572
> sqrt (9)
3.00009155413138
> sqrt (94 + 87)
13,452424188555612
> sqrt (144)
12.000000012408687
As funções parecem estar funcionando bem. No entanto, existe uma ideia melhor à espreita aqui. Essas funções são todas escritas independentemente, mesmo que sejam destinadas a trabalhar em conjunto umas com as outras. Provavelmente, não usaremos a função isGoodEnough () com nenhum outro conjunto de funções ou por conta própria. Além disso, a única função que interessa ao usuário é a função sqrt (), já que é aquela que é chamada para encontrar uma raiz quadrada.

O escopo do bloco oculta as funções auxiliares
A solução aqui é usar o escopo do bloco para definir todas as funções auxiliares necessárias dentro do bloco da função sqrt (). Vamos remover square () e average () da definição, já que essas funções podem ser úteis em outras definições de função e não são tão limitadas para uso em um algoritmo que implemente o método de Newton. Aqui está a definição da função sqrt () agora com as outras funções auxiliares definidas dentro do escopo de sqrt ():

função sqrt (x) {
função melhorar (acho, x) {
média de retorno (palpite, (x / palpite));
}
função isGoodEnough (guess, x) {
return (Math.abs (quadrado))> 0,001;
}
função sqrt_iter (guess, x) {
if (isGoodEnough (acho, x)) {
devolver palpite;
}
outro {
return sqrt_iter (melhorar (suposição, x), x);
}
}
return sqrt_iter (1.0, x);
}
Agora podemos carregar este programa em nosso shell e calcular algumas raízes quadradas:

> .load sqrt_iter.js
> sqrt (9)
3.00009155413138
> sqrt (2)
1,4142156862745097
> sqrt (3,14159)
1,772581833543688
> sqrt (144)
12.000000012408687
Observe que você não pode chamar nenhuma das funções auxiliares de fora da função sqrt ():

> sqrt (9)
3.00009155413138
> sqrt (2)
1,4142156862745097
> melhorar (1,2)
ReferenceError: melhorar não está definido
> isGoodEnough (1,414, 2)
ReferenceError: isGoodEnough não está definido
Como as definições dessas funções (improve () e isGoodEnough ()) foram movidas dentro do escopo de sqrt (), elas não podem ser acessadas em um nível mais alto. Naturalmente, você pode mover qualquer uma das definições da função auxiliar fora da função sqrt () para ter acesso a elas globalmente, como fizemos com average () e square ().

Melhoramos muito nossa implementação do método de Newton, mas ainda temos mais uma coisa que podemos fazer para melhorar nossa função sqrt () simplificando-a ainda mais aproveitando o escopo léxico.

Melhorando a clareza com o escopo léxico
O conceito por trás do escopo léxico é que, quando uma variável é vinculada a um ambiente, outros procedimentos (funções) definidos nesse ambiente têm acesso ao valor dessa variável. Isso significa que na função sqrt (), o parâmetro x está vinculado a essa função, o que significa que qualquer outra função definida dentro do corpo de sqrt () pode acessar x.

Sabendo disso, podemos simplificar ainda mais a definição de sqrt () removendo todas as referências a x nas definições de função, já que x é agora uma variável livre e acessível por todas elas. Aqui está nossa nova definição de sqrt ():

função sqrt (x) {
A função isGoodEnough (guess) {
return (Math.abs (quadrado))> 0,001;
}
function melhorar (acho) {
média de retorno (palpite, (x / palpite));
}
função sqrt_iter (guess) {
if (isGoodEnough (acho)) {
devolver palpite;
}
outro {
retornar sqrt_iter (melhorar (adivinhar));
}
}
retornar sqrt_iter (1.0);
}
As únicas referências ao parâmetro x estão nos cálculos onde o valor de x é necessário. Vamos carregar essa nova definição no shell e testá-la:

> .load sqrt_iter.js
> sqrt (9)
3.00009155413138
> sqrt (2)
1,4142156862745097
> sqrt (123 + 37)
12,649110680047308
> sqrt (144)
12.000000012408687
Escopo lexical e estrutura de bloco são características importantes do JavaScript e nos permitem construir programas que são mais fáceis de entender e gerenciar. Isso é especialmente importante quando começamos a construir programas maiores e mais complexos.