A palavra "poço" significa "armadilha". Devido à natureza da "linguagem fraca" do JavaScript, é extremamente solta e flexível durante o uso, mas também é extremamente fácil de "ser atingido". Esses poços geralmente estão escondidos, então você deve manter os olhos abertos para a navegação suave na estrada de aprender e aplicar JS.
1. Variáveis globais
O JavaScript gerencia os escopos através de funções. As variáveis declaradas dentro de uma função estão apenas dentro dessa função e não estão disponíveis fora da função. Por outro lado, as variáveis globais são declaradas fora de qualquer função ou são usadas de maneira simples e simplesmente sem declaração.
"Use -o simplesmente sem declarar" significa que as variáveis são declaradas sem usar a palavra -chave VAR. Já deixamos claro que a maneira de evitar implicitamente geração de variáveis globais é declarar variáveis com a palavra -chave VAR o máximo possível.
Mas você acha que não há problema em usar o VAR? Vamos dar uma olhada neste poço:
A cópia do código é a seguinte:
function foo () {
var a = b = 0;
// corpo...
}
Talvez o que você espera sejam duas variáveis locais, mas B é uma variável global real. Por que? Como a operação de atribuição é da direita para a esquerda, então isso é equivalente a:
A cópia do código é a seguinte:
function foo () {
var a = (b = 0);
// corpo...
}
Portanto, B é uma variável global.
Preencha o poço: declarações variáveis, é melhor vir uma a uma, não faça no atacado ~ _ ~;
2. Declaração variável
Vamos dar uma olhada no poço primeiro:
A cópia do código é a seguinte:
myname = "global";
function foo () {
alerta (myname);
var myname = "local";
alerta (myname);
}
foo ();
À primeira vista, esperamos que os resultados de dois alertas sejam "globais" e "locais", respectivamente, mas os resultados reais são "indefinidos" e "locais". Por que? Porque as variáveis são declaradas no mesmo escopo (a mesma função) e são divulgadas pela parte superior do escopo.
Portanto, o comportamento de execução do snippet de código acima pode ser assim:
A cópia do código é a seguinte:
function foo () {
var myname;
alerta (myname); // "indefinido"
myname = "local";
alerta (myname); // "Local"
}
Use outro poço para testar se você realmente entende o pré-parente:
A cópia do código é a seguinte:
if (! ("A" na janela)) {
var a = 1;
}
alerta (a);
A declaração de uma variável é avançada para o topo do código e ainda não foi atribuída. Em seguida, digite a declaração IF e julgue que o "A" na janela é estabelecido (a foi declarado como uma variável global), portanto o resultado do cálculo da declaração de julgamento é falso e a declaração IF será desativada; o valor de A é indefinido.
A cópia do código é a seguinte:
var a; // "indefinido"
console.log ("a" na janela); // verdadeiro
if (! ("A" na janela)) {
var a = 1; // não executado
}
alerta (a); // "indefinido"
Preencha o poço: é melhor colocar manualmente a declaração variável no topo do escopo. Para variáveis que não podem ser atribuídas no momento, você pode usar o método de declarar primeiro e depois atribuir valores.
3. Declaração da função
As declarações de função também são avançadas para o topo do escopo e são analisadas e avaliadas antes que quaisquer expressões e declarações sejam analisadas e avaliadas.
A cópia do código é a seguinte:
alerta (tipo de foo); // "função"
function foo () {
// corpo...
}
Você pode compará -lo:
A cópia do código é a seguinte:
alerta (tipo de foo); // "indefinido"
var foo = function () {
// corpo...
};
Se você entender esse princípio, você ainda cairá nas seguintes armadilhas?
A cópia do código é a seguinte:
function test () {
alerta ("1");
}
teste();
function test () {
alerta ("2");
}
teste();
Ao executar o trecho de código acima, as janelas pop-up que você vê são "2", por que elas não são "1" e "2", respectivamente? É muito simples, a declaração de teste é analisada antes do teste (). Como o último substitui o primeiro, o resultado de ambas as execuções é "2".
Preencha o poço: na maioria dos casos, uso expressões de função em vez de declarações de função, especialmente em alguns blocos de declaração.
4. Expressões de função
Vejamos primeiro a expressão de função nomeada. Claro, ele deve ter um nome, por exemplo:
Copie o código da seguinte forma: var bar = function foo () {
// corpo...
};
Deve -se notar que o nome da função é visível apenas para sua função dentro. Como as seguintes armadilhas:
A cópia do código é a seguinte:
var bar = function foo () {
foo (); // executa normalmente
};
foo (); // Erro: ReferenceError
Preencha o poço: tente usar expressões de função nomeadas o mínimo possível (exceto para alguns fins de recursão e depuração) e nunca use nomes de funções externamente.
5. Auto-execução da função
Para expressões de função, você pode executar adicionando () depois, e os parâmetros podem ser passados entre parênteses, enquanto a declaração de função não pode. poço:
A cópia do código é a seguinte:
// (1) Este é apenas um operador de agrupamento, não uma chamada de função!
// então a função aqui não foi executada, ainda é uma declaração
função foo (x) {
alerta (x);
} (1);
Os seguintes trechos de código são executados separadamente e a janela pop-up mostra "1". Porque antes (1), são expressões de função, portanto, o () aqui não é um operador de agrupamento, mas um operador, indicando a execução da chamada.
Copie o código da seguinte forma: // Expressão de função anônima padrão
var bar = função foo (x) {
alerta (x);
} (1);
// o anterior () converte a declaração de função em uma expressão
(função foo (x) {
alerta (x);
}) (1);
// o inteiro () é uma expressão
(função foo (x) {
alerta (x);
} (1));
// nova expressão
nova função foo (x) {
alerta (x);
} (1);
// &&, || ,!, +, -, ~ etc. Operadores (e vírgulas) para desambiguar expressões de função e declarações de funções
// Então, uma vez que o analisador saiba que um deles é uma expressão, os outros também não atacam expressões.
true && function foo (x) {
alerta (x);
} (1);
Encha o poço: a chave para este poço é descobrir a essência de todos os tipos de expressões de função.
6. Fechamento no loop
O seguinte demonstra uma armadilha comum:
A cópia do código é a seguinte:
<! doctype html>
<html lang = "en">
<head>
<meta charset = "utf-8">
<title> Documento </title>
</head>
<Body>
<H3> Ao clicar nos links abaixo, mostre o número de sua sequência </h3>
<ul>
<li> <a href = " #"> link #0 </a> </li>
<li> <a href = " #"> link #1 </a> </li>
<li> <a href = " #"> link #2 </a> </li>
<li> <a href = " #"> link #3 </a> </li>
<li> <a href = " #"> link #4 </a> </li>
</ul>
</body>
</html>
A cópia do código é a seguinte:
var links = document.getElementsByTagName ("ul") [0] .getElementsByTagName ("A");
for (var i = 0, l = links.length; i <l; i ++) {
links [i] .OnClick = function (e) {
E.PreventDefault ();
alerta ("você clica em link #" + i);
}
}
Esperamos que, ao clicar no i-ésimo link, obtemos o valor do índice I nesta sequência. No entanto, não importa qual link seja clicado, obtemos o resultado final de I após o loop: "5".
Explique o motivo: quando o alerta é chamado, a expressão da função anônima no loop for mantém uma referência à variável externa i (fechamento). Neste momento, o loop terminou e o valor de I é modificado para "5".
Preencha o poço: para obter o resultado desejado, você precisa criar uma cópia da variável i em cada loop. O seguinte demonstra a abordagem correta:
A cópia do código é a seguinte:
<head>
<meta charset = "utf-8">
<title> Documento </title>
</head>
<Body>
<H3> Ao clicar nos links abaixo, mostre o número de sua sequência </h3>
<ul>
<li> <a href = " #"> link #0 </a> </li>
<li> <a href = " #"> link #1 </a> </li>
<li> <a href = " #"> link #2 </a> </li>
<li> <a href = " #"> link #3 </a> </li>
<li> <a href = " #"> link #4 </a> </li>
</ul>
</body>
</html>
A cópia do código é a seguinte:
var links = document.getElementsByTagName ("ul") [0] .getElementsByTagName ("A");
for (var i = 0, l = links.length; i <l; i ++) {
links [i] .OnClick = (function (index) {
Função de retorno (e) {
E.PreventDefault ();
alerta ("você clica em link #" + índice);
}
})(eu);
}
Como você pode ver, a forma de (function () {...}) () é a autoexecução da função mencionada acima. Eu é passado para indexar como um parâmetro. Quando o alerta é executado novamente, ele tem uma referência ao índice. Neste momento, esse valor não será alterado pelo loop. Obviamente, depois de entender o princípio, você também pode escrever assim:
A cópia do código é a seguinte:
for (var i = 0, l = links.length; i <l; i ++) {
(function (index) {
links [i] .OnClick = function (e) {
E.PreventDefault ();
alerta ("você clica em link #" + índice);
}
})(eu);
}
Funciona também.