O escopo é um dos conceitos mais importantes em JavaScript. Se você deseja aprender bem o JavaScript, precisa entender como funcionam o escopo e as cadeias de escopo do JavaScript. O artigo de hoje fornece uma breve introdução ao escopo e cadeia de escopo do JavaScript, na esperança de ajudar todos a aprender melhor o JavaScript.
Escopo JavaScript
Qualquer linguagem de programação possui o conceito de escopo. Simplificando, escopo é a gama acessível de variáveis e funções. Em JavaScript, existem dois tipos de escopo de variável: escopo global e escopo local.
1. Escopo Global
Objetos que podem ser acessados em qualquer lugar do código têm escopo global. De modo geral, as seguintes situações têm escopo global:
(1) A função mais externa e as variáveis definidas fora da função mais externa têm escopo global, por exemplo:
Copie o código do código da seguinte forma:
varauthorName="Córrego da Montanha";
functiondoSomething(){
varblogName="";
functioninnerSay(){
alerta(blogName);
}
interiorSay();
}
alert(authorName); //Riacho na montanha
alert(blogName); // Erro de script
façaAlguma coisa();//
innerSay()//erro de script
(2) Todas as variáveis que não são definidas e atribuídas diretamente são automaticamente declaradas como tendo escopo global, por exemplo:
Copie o código do código da seguinte forma:
functiondoSomething(){
varauthorName="Córrego da Montanha";
blogName="";
alerta(autorNome);
}
alerta(blogName); //
alert(autorNome); // Erro de script
A variável blogName tem escopo global, mas authorName não pode ser acessado fora da função.
(3) Todas as propriedades dos objetos de janela têm escopo global
Geralmente, as propriedades integradas do objeto janela têm escopo global, como window.name, window.location, window.top, etc.
2. Escopo Local
Ao contrário do escopo global, o escopo local geralmente só é acessível dentro de um trecho fixo de código, mais comumente dentro de uma função, portanto, em alguns lugares você também verá pessoas se referindo a esse escopo como escopo de função, como o código a seguir. blogName e a função innerSay in têm apenas escopo local.
Copie o código do código da seguinte forma:
functiondoSomething(){
varblogName="";
functioninnerSay(){
alerta(blogName);
}
interiorSay();
}
alert(blogName); // Erro de script
innerSay();//erro de script
Cadeia de Escopo
Em JavaScript, funções também são objetos. Na verdade, tudo em JavaScript é um objeto. Os objetos de função, como outros objetos, possuem propriedades que podem ser acessadas por meio de código e um conjunto de propriedades internas que só são acessíveis ao mecanismo JavaScript. Uma das propriedades internas é [[Scope]], definida pela terceira edição do padrão ECMA-262. Esta propriedade interna contém a coleção de objetos no escopo em que a função é criada. , que determina quais dados podem ser acessados pela função.
Quando uma função é criada, sua cadeia de escopo é preenchida com objetos de dados acessíveis no escopo no qual a função foi criada. Por exemplo, defina a seguinte função:
Copie o código do código da seguinte forma:
funçãoadicionar(num1,num2){
varsum=num1+num2;
soma de retorno;
}
Quando a função add for criada, sua cadeia de escopo será preenchida com um objeto global, que contém todas as variáveis globais, conforme mostrado na figura a seguir (nota: a imagem ilustra apenas uma parte de todas as variáveis):
O escopo da função add será usado durante a execução. Por exemplo, execute o seguinte código:
Copie o código do código da seguinte forma:
var total = soma(5,10);
Quando esta função é executada, um objeto interno denominado "contexto de execução" é criado. O contexto de tempo de execução define o ambiente no qual a função é executada. Cada contexto de tempo de execução tem sua própria cadeia de escopo para resolução de identificador. Quando um contexto de tempo de execução é criado, sua cadeia de escopo é inicializada para o objeto contido em [[Escopo]] da função atualmente em execução.
Os valores são copiados na cadeia de escopo do contexto de tempo de execução na ordem em que aparecem na função. Juntos, eles formam um novo objeto chamado "objeto de ativação". Este objeto contém todas as variáveis locais, parâmetros nomeados, coleções de parâmetros e isso da função. Em seguida, esse objeto será enviado para o front-end da cadeia de escopo. for destruído, o objeto ativo também será destruído. A nova cadeia de escopo é mostrada abaixo:
Durante a execução da função, caso uma variável não seja encontrada, ela passará por um processo de resolução de identificador para determinar onde obter e armazenar os dados. Este processo começa no início da cadeia de escopo, ou seja, no objeto ativo, e procura um identificador com o mesmo nome. Se for encontrado, use a variável correspondente a este identificador. procure o próximo objeto na cadeia de escopo. If Se nenhum objeto for encontrado após a pesquisa, o identificador será considerado indefinido. Durante a execução da função, cada identificador passa por esse processo de busca.
Encadeamento de escopo e otimização de código
Pode-se observar pela estrutura da cadeia de escopo que quanto mais profundo o identificador estiver localizado na cadeia de escopo do contexto de tempo de execução, mais lenta será a velocidade de leitura e gravação. Conforme mostrado na figura acima, como as variáveis globais sempre existem no final da cadeia de escopo do contexto de tempo de execução, encontrar variáveis globais é o mais lento durante a resolução do identificador. Portanto, ao escrever código, você deve usar variáveis globais o mínimo possível e variáveis locais tanto quanto possível. Uma boa regra é: se um objeto de escopo cruzado for referenciado mais de uma vez, armazene-o em uma variável local antes de usá-lo. Por exemplo o seguinte código:
Copie o código do código da seguinte forma:
functionchangeColor(){
document.getElementById("btnChange").onclick=function(){
document.getElementById("targetCanvas").style.backgroundColor="red";
};
}
Esta função faz referência ao documento da variável global duas vezes. Para encontrar a variável, você deve percorrer toda a cadeia de escopo até que ela seja finalmente encontrada no objeto global. Este código pode ser reescrito da seguinte forma:
Copie o código do código da seguinte forma:
functionchangeColor(){
vardoc=documento;
doc.getElementById("btnChange").onclick=function(){
doc.getElementById("targetCanvas").style.backgroundColor="red";
};
}
Este código é relativamente simples e não apresentará uma grande melhoria de desempenho após a reescrita. No entanto, se houver um grande número de variáveis globais no programa que são acessadas repetidamente, o desempenho do código reescrito será significativamente melhorado.
cadeia de escopo de mudança
O contexto de tempo de execução correspondente é único cada vez que uma função é executada, portanto, chamar a mesma função várias vezes resultará na criação de vários contextos de tempo de execução. Quando a função concluir a execução, o contexto de execução será destruído. Cada contexto de tempo de execução está associado a uma cadeia de escopo. Em circunstâncias normais, durante a execução do contexto de tempo de execução, sua cadeia de escopo só será afetada pela instrução with e pela instrução catch.
A instrução with é um atalho para usar objetos para evitar escrever código repetido. Por exemplo:
Copie o código do código da seguinte forma:
functioninitUI(){
com(documento){
varbd=corpo,
links=getElementsByTagName("a"),
eu=0,
len=links.comprimento;
enquanto(i<len){
atualizar(links[i++]);
}
getElementById("btnInit").onclick=function(){
façaAlgo();
};
}
}
A instrução width é usada aqui para evitar escrever o documento várias vezes, o que parece mais eficiente, mas na verdade causa problemas de desempenho.
Quando o código atinge a instrução with, a cadeia de escopo do contexto de tempo de execução é temporariamente alterada. É criado um novo objeto mutável que contém todas as propriedades do objeto especificado pelo parâmetro. Este objeto será colocado no topo da cadeia de escopo, o que significa que todas as variáveis locais da função estão agora no segundo objeto da cadeia de escopo e, portanto, são mais caras para acessar. Conforme mostrado abaixo:
Portanto, você deve evitar usar a instrução with em seu programa, simplesmente armazenar o documento em uma variável local pode melhorar o desempenho.
Outra coisa que muda a cadeia de escopo é a instrução catch na instrução try-catch. Quando ocorre um erro no bloco de código try, o processo de execução salta para a instrução catch e, em seguida, o objeto de exceção é colocado em um objeto mutável e colocado no topo do escopo. Dentro do bloco catch, todas as variáveis locais da função serão colocadas no segundo objeto da cadeia de escopo. Código de exemplo:
Copie o código do código da seguinte forma:
tentar{
façaAlgo();
}pegar(ex){
alert(ex.message);//A cadeia de escopo muda aqui
}
Observe que assim que a instrução catch for executada, a cadeia de escopo retornará ao seu estado anterior. A instrução try-catch é muito útil na depuração de código e tratamento de exceções, portanto não é recomendado evitá-la completamente. Você pode reduzir o impacto no desempenho das instruções catch otimizando seu código. Um bom padrão é delegar o tratamento de erros a uma função, por exemplo:
Copie o código do código da seguinte forma:
tentar{
façaAlgo();
}pegar(ex){
handleError(ex); // Delega ao método do processador
}
No código otimizado, o método handleError é o único código executado na cláusula catch. Esta função recebe como parâmetro um objeto de exceção, para que você possa tratar os erros de maneira mais flexível e uniforme. Como apenas uma instrução é executada e nenhuma variável local é acessada, alterações temporárias na cadeia de escopo não afetarão o desempenho do código.