Escopo
O escopo é o escopo da função de uma variável e função. Todas as variáveis declaradas em uma função em JavaScript são sempre visíveis no corpo da função. Existem escopos globais e escopos locais no JavaScript, mas não há escopo no nível do bloco. A prioridade das variáveis locais é maior que a das variáveis globais. Através de vários exemplos, podemos entender as "regras não ditas" do escopo em JavaScript (essas também são perguntas que geralmente são feitas em entrevistas de front-end).
1. Declaração variável com antecedência
Exemplo 1:
var scope = "global"; function scopetest () {console.log (escopo); var scope = "local"} scopetest (); //indefinidoA saída aqui é indefinida e não há erro. Isso ocorre porque as declarações na função que mencionamos acima são sempre visíveis no corpo da função. A função acima é equivalente a:
var scope = "global"; function scopetest () {var scope; console.log (escopo); scope = "local"} scopetest (); //localObserve que se o VAR for esquecido, a variável será declarada como uma variável global.
2. Sem escopo no nível do bloco
Ao contrário de outros idiomas que usamos comumente, não há escopo no nível de bloco em JavaScript:
function scopetest () {var scope = {}; if (scope instância do objeto) {var j = 1; for (var i = 0; i <10; i ++) {//console.log(i); } console.log (i); // saída 10} console.log (j); // saída 1}No JavaScript, o escopo da função das variáveis é o nível da função, ou seja, todas as variáveis na função são definidas em toda a função, o que também traz algumas "regras não ditas" que encontraremos se não tomarmos cuidado:
var scope = "hello"; function scopetest () {console.log (escopo); // ① var scope = "não"; console.log (escopo); // ②}A saída de valor em ① era realmente indefinida, o que é loucura. Definimos o valor da variável global. Este lugar não deveria ser olá? De fato, o código acima é equivalente a:
var scope = "hello"; function scopetest () {var scope; console.log (escopo); // ① scope = "não"; console.log (escopo); // ②}Declare as variáveis precoces e globais têm prioridade mais baixa do que as variáveis locais. De acordo com essas duas regras, não é difícil entender por que a saída é indefinida.
Cadeia de escopo
No JavaScript, cada função tem seu próprio contexto de execução. Quando o código é executado nesse ambiente, uma cadeia de objetos variáveis de escopo será criada. A cadeia de escopo é uma lista de objetos ou cadeia de objetos, que garante acesso ordenado a objetos variáveis.
A extremidade frontal da cadeia do escopo é o objeto variável do ambiente de execução de código atual, que é frequentemente chamado de "objeto ativo". A busca por variáveis inicia a partir do objeto da primeira cadeia. Se o objeto contiver atributos variáveis, a pesquisa será interrompida. Caso contrário, a pesquisa continuará a procurar a cadeia de escopo superior até que o objeto global seja encontrado:
A busca por cadeias de escopo passo a passo também afetará o desempenho do programa. Quanto mais tempo a cadeia de variáveis do escopo, maior o impacto no desempenho. Essa também é uma das principais razões pelas quais tentamos evitar o uso de variáveis globais.
Encerramento
Conceitos básicos
O escopo é um pré -requisito para entender o fechamento. Os fechamentos referem -se à capacidade de acessar variáveis no escopo externo dentro do escopo atual.
function createClosure () {var name = "jack"; return {setStr: function () {name = "rose"; }, getStr: function () {return name + ": hello"; }}} var Builder = new CreateClosure (); Builder.SetStr (); Console.log (Builder.getStr ()); // Rose: OláO exemplo acima retorna dois fechamentos na função, os quais mantêm referências ao escopo externo; portanto, as variáveis na função externa são sempre acessíveis onde quer que sejam chamadas. As funções definidas dentro de uma função adicionarão o objeto ativo da função externa à sua própria cadeia de escopo. Portanto, no exemplo acima, a função interna pode acessar as propriedades da função externa através da função interna. Esta também é uma maneira de JavaScript simular variáveis privadas.
Nota: Como os fechamentos terão escopos adicionais de funções (funções anônimas internas carregam escopos de funções externas), o fechamento ocupará mais espaço de memória do que outras funções e o uso excessivo pode levar ao aumento do uso da memória.
Variáveis nos fechamentos
Ao usar o fechamento, devido à influência do mecanismo da cadeia de escopo, o fechamento pode obter apenas o último valor da função interna. Um efeito colateral disso é que, se a função interna estiver em um loop, o valor da variável é sempre o último valor.
// Esta instância não é razoável e tem certos fatores de atraso. Isso é principalmente para ilustrar os problemas na função do loop de fechamento timemanage () {for (var i = 0; i <5; i ++) {setTimeout (function () {console.log (i);}, 1000)}; }O programa acima não insere números 1-5 como esperávamos, mas produz 5 todas as 5 vezes. Vamos dar uma olhada em outro exemplo:
function createClosure () {var resultado = []; for (var i = 0; i <5; i ++) {resultado [i] = function () {return i; }} Retornar resultado;}Chamando CreateClosure () [0] () retorna 5, e CreateClosure () [4] () Retorna O valor ainda é 5. A partir dos dois exemplos acima, podemos ver o problema que o fechamento existe ao usar funções internas com loops: como a cadeia de escopo de cada função armazena objetos ativos para as funções externas (Timemanage, CreatEcloure), elas se referem a todos os objetos ativos para as mesmas funções (Timemanage. Quando a função externa retorna, o valor de I neste momento é 5; portanto, o valor de cada função interna i também é 5.
Então, como resolver esse problema? Podemos forçar o retorno do resultado esperado através de um invólucro anônimo (expressão de função auto-executiva anônima):
função timemanage () {for (var i = 0; i <5; i ++) {(function (num) {setTimeout (function () {console.log (num);}, 1000);}) (i); }}Ou devolver uma atribuição de função anônima na função anônima de fechamento:
função timemanage () {for (var i = 0; i <10; i ++) {setTimeout ((function (e) {return function () {console.log (e);}}) (i), 1000)}} // timemanager (); Saída 1,2,3,4,5função createClosure () {var resultado = []; for (var i = 0; i <5; i ++) {resultado [i] = function (num) {return function () {console.log (num); } }(eu); } resultado de retorno;} // createClosure () [1] () saída 1; createClosure () [2] () Saída 2Seja um invólucro anônimo ou funções anônimas anônimas, em princípio, uma vez que a função é passada pelo valor, o valor da variável que serei copiado para o número de parâmetro real e uma função anônima é criada dentro da função anônima para retornar num, para que cada função tenha uma cópia de Num, que não se afetará.
Isso está fechando
Preste atenção especial ao usar isso nos fechamentos, pois um pouco de descuido pode causar problemas. Geralmente entendemos que esse objeto está vinculado por função em tempo de execução. Na função global, esse objeto é um objeto de janela. Quando a função é chamada de método no objeto, isso é igual a esse objeto (o TODO faz um processo de classificação sobre isso). Como o escopo das funções anônimas é global, esse fechamento geralmente aponta para a janela de objeto global:
var scope = "global"; var object = {scope: "local", getScope: function () {return function () {return this.Scope; }}}Calling Object.getScope () () retorna o valor global em vez do local que esperávamos. Dissemos anteriormente que as funções anônimas internas no fechamento terão o escopo da função externa, então por que não conseguir isso da função externa? Quando cada função é chamada, isso e os argumentos serão criados automaticamente. Ao procurar funções anônimas internas, eles pesquisam as variáveis que queremos no objeto ativo. Portanto, pare de procurar funções externas e nunca é possível acessar diretamente variáveis em funções externas. Em suma, quando uma função é chamada de método de objeto em um fechamento, é importante prestar atenção especial que isso na função anônima dentro do método aponte para uma variável global.
Felizmente, podemos resolver esse problema de maneira muito simples, basta armazenar isso no escopo da função externa em uma variável que pode ser acessada por um fechamento:
var scope = "global"; var object = {scope: "local", getScope: function () {var que = this; return function () {return that.Scope; }}} object.getScope () () () retorna o valor local.Memória e desempenho
Como o fechamento contém a mesma referência da cadeia de escopo que o contexto de tempo de execução da função, ela terá um certo efeito negativo. Quando o objeto ativo e o contexto de tempo de execução na função são destruídos, o objeto ativo não pode ser destruído porque ainda há uma referência ao objeto ativo, o que significa que o fechamento ocupa mais espaço de memória do que as funções comuns e também pode causar vazamento de memória no navegador do IE, como segue:
function bindEvent () {var no destino = document.getElementById ("elem"); Target.OnClick = function () {console.log (Target.name); }}No exemplo acima, a função anônima gera uma referência ao destino do objeto externo. Enquanto a função anônima existir, a referência não desaparecerá e o objeto alvo da função externa não será destruído, o que cria uma referência circular. A solução é reduzir as referências circulares a variáveis externas, criando uma cópia do Target.name e redefinir manualmente o objeto:
function bindEvent () {var no destino = document.getElementById ("elem"); var name = Target.name; Target.OnClick = function () {console.log (nome); } alvo = nulo; }Se houver acesso a variáveis externas no fechamento, o caminho de pesquisa para identificadores será, sem dúvida, adicionado e, sob determinadas circunstâncias, isso também causará perdas de desempenho. Mencionamos anteriormente: tente armazenar variáveis externas em variáveis locais para reduzir o comprimento de pesquisa das cadeias de escopo.
Resumo: Os fechamentos não são exclusivos do JavaScript, mas eles têm suas próprias manifestações únicas em JavaScript. Usando fechamentos, podemos definir algumas variáveis privadas em JavaScript e até imitar escopos no nível do bloco. No entanto, durante o uso de fechamentos, também precisamos entender os problemas existentes para evitar problemas desnecessários.