O que é escopo?
Escopo: quais variáveis são acessíveis a partir de um determinado ponto do código.
O escopo é um daqueles tópicos importantes em Javascript. Minha esperança é que, depois de ler este documento, você saiba como e quando manipular o escopo a seu favor e onde colocar suas definições de variáveis.
Vamos dar uma olhada neste exemplo:
function this_land(){
land = great;
console.log(land);
var land;
}
this_land();
> great
console.log(land);
> ReferenceError: land is not defined.
Primeiro, estamos definindo uma função, chamada this_land. Por enquanto, tudo bem. Mas por que estamos usando terras antes de declararmos?
Todas as variáveis (incluindo funções) em Javascript são efetivamente movidas para o topo do bloco em que estão. Normalmente, esse recurso fica fora do caminho, mas é útil saber que mesmo se você definir uma variável na parte inferior de um função, ele já estará acessível para outros elementos no mesmo bloco.
Se alguém fizesse isso, ficaria confuso, e não fazemos. Isso é apenas para fins de demonstração.
Mas que tal tentar acessar a variável “terreno” fora da função? Isso é algo que às vezes temos que fazer, já que não queremos enlouquecer passando tudo em parâmetros todas as vezes. Especialmente com funções profundamente aninhadas, é conveniente apenas usá-las.
Usamos var para definir “terreno” e, portanto, tornou-se privado para a função this_land. Se tivéssemos deixado de fora a var na frente do terreno, poderíamos ter acessado o terreno, após a execução da função.
Como o escopo é geralmente usado
Como regra geral, não há problema em acessar as variáveis de uma função externa, mas não de uma função interna.
function outer(){
var some_var;
var some_other_var;
some_var = 1;
some_other_var = 2;
var inner = function(){
return some_var + some_other_var;
};
return inner();
}
outer();
Sim, as funções podem ser atribuídas a uma variável ou não, mas isso está fora do escopo deste exemplo. Muahahaha.
Você vê agora o que significa externo e interno. Portanto, interno pode acessar externo, mas externo apenas obtém o valor de retorno interno, não acessando suas variáveis.
Truques suculentos
Funções anônimas
Este pedaço de sintaxe
function(){
var something = 0;
return something;
}
é chamada de função anônima porque não tem nome. Você pode ter visto algo assim em setTimeout ou setInterval. Eval é desaprovado por uma variedade de razões e, como setTimeout usa eval se você passar uma string para eles, essas formas de setTimeout e setInterval também são desaprovadas. Em vez disso, usamos funções regulares, mas por conveniência omitimos o nome. Os retornos de chamada do jquery também fazem muito isso:
$.ajax(‘someurl’, {
done: function(data){
derp derp
}
});
Expressões de função imediatamente invocadas
fazem uso de funções anônimas e têm esta aparência:
(function(){
// function body
})();
Sem dúvida, você viu isso:
(function($){
// interact with the page
})(jQuery);
jQuery é definido em outro lugar, provavelmente em jquery.min.js e apenas no caso de outras bibliotecas usarem a variável $ também, atribuímos especificamente jQuery a $ para o escopo de nossa função.
Observe que poderíamos ter escrito
var do_something_using_jquery = function($){
// interact with the page
};
(do_something_using_jquery)(jQuery);
Temos uma função e, acrescentando o parêntese no final, imediatamente a chamamos.
Para que serve isso?
Preservando escopo
Digamos que temos uma função que depende de um escopo externo para algumas de suas variáveis …
Além disso, não sabemos quando essa função será chamada, mas temos todas as variáveis de que ela precisa disponíveis agora.
Problema: as variáveis externas podem ter mudado no momento em que a função interna é chamada.
var fns = []; // an array of functions to be called later
for(var i=0; i<10; i++){
var fn = (function(a){
return function(){
return a*2;
};
})(i);
}
// ... let some time pass
for(var i=0; i<fns.length; i++){
var doubled = fns[i]()
}
Este é um exemplo complicado. Vou trocá-lo por um exemplo real quando encontrar um em nossa base de código.
Simulando membros privados e públicos
var smells_like_a_proper_object = (function(){
var private1 = "outer has no business changing this";
return {
public_access_to_private_member: function(){
return private1; // private1 is in scope for the readPrivate1 function, which is public
},
another_public_function: function(arg){
return private1 + arg;
},
a_public_member: 42
}
})();
console.log(smells_like_a_proper_object.public_access_to_private_member()); // should work
console.log(smells_like_a_proper_object.private1); // should through an error.
Agora podemos acessar tudo o que é retornado da função expressen imediatamente invocada (IIFE), mas nada que seja meramente definido dentro dela.
Precisamos de mais trabalho para chegar à variável privada, mas estamos no controle do que pode vazar para fora. Contanto que nos lembremos de variar nossas variáveis, não corremos o risco de ter partes externas do código alterando os valores de que precisamos.
E é isso mesmo!