La palabra "pozo" significa "trampa". Debido a la naturaleza del "lenguaje débil" de JavaScript, es extremadamente suelto y flexible durante el uso, pero también es extremadamente fácil "ser golpeado". Estos pozos a menudo están ocultos, por lo que debe mantener los ojos abiertos a la navegación suave en el camino del aprendizaje y la aplicación de JS.
1. Variables globales
JavaScript administra los ámbitos a través de funciones. Las variables declaradas dentro de una función están solo dentro de esta función y no están disponibles fuera de la función. Por otro lado, las variables globales se declaran fuera de cualquier función o se usan de manera simple y simplemente sin declaración.
"Úselo simplemente sin declarar" significa que las variables se declaran sin usar la palabra clave var. Ya hemos dejado en claro que la forma de evitar la generación de variables globales implícitamente es declarar variables con la palabra clave VAR tanto como sea posible.
¿Pero crees que está bien usar var? Echemos un vistazo a este pozo:
La copia del código es la siguiente:
función foo () {
var a = b = 0;
// cuerpo...
}
Tal vez lo que esperas son dos variables locales, pero B es una verdadera variable global. ¿Por qué? Porque la operación de asignación es de derecha a izquierda, esto es equivalente a:
La copia del código es la siguiente:
función foo () {
var a = (b = 0);
// cuerpo...
}
Entonces B es una variable global.
Complete el pozo: declaraciones variables, es mejor venir una por una, no hagas al por mayor ~ _ ~;
2. Declaración variable
Echemos un vistazo al pozo primero:
La copia del código es la siguiente:
myname = "global";
función foo () {
alerta (myname);
var myname = "local";
alerta (myname);
}
foo ();
A primera vista, esperamos esperar que los resultados de dos alertas sean "globales" y "locales", respectivamente, pero los resultados reales están "indefinidos" y "locales". ¿Por qué? Porque las variables se declaran en el mismo alcance (misma función), y primero se analizan por la parte superior del alcance.
Entonces, el comportamiento de ejecución del fragmento de código anterior puede verse así:
La copia del código es la siguiente:
función foo () {
var myname;
alerta (myname); // "indefinido"
myname = "local";
alerta (myname); // "local"
}
Use otro pozo para probar si realmente entiende el pre-parro:
La copia del código es la siguiente:
if (! ("a" en la ventana)) {
var a = 1;
}
alerta (a);
La declaración de una variable avanza a la parte superior del código y aún no se ha asignado. A continuación, ingrese la declaración IF, y juzgue que se establece el "A" en la ventana (A se ha declarado como una variable global), por lo que el resultado del cálculo de la declaración del juicio es falso, y la declaración IF saltará, por lo que el valor de A está indefinido.
La copia del código es la siguiente:
var a; // "indefinido"
console.log ("A" en la ventana); // verdadero
if (! ("a" en la ventana)) {
var a = 1; // no ejecutado
}
alerta (a); // "indefinido"
Complete el pozo: es mejor colocar manualmente la declaración variable en la parte superior del alcance. Para las variables que no se pueden asignar en este momento, puede usar el método para declarar primero y luego asignar valores.
3. Declaración de funciones
Las declaraciones de la función también avanzan a la parte superior del alcance, y se analizan y evalúan antes de que cualquier expresión y declaración se analice y evalúen.
La copia del código es la siguiente:
alerta (typeof foo); // "función"
función foo () {
// cuerpo...
}
Puedes compararlo:
La copia del código es la siguiente:
alerta (typeof foo); // "indefinido"
var foo = function () {
// cuerpo...
};
Si comprende este principio, ¿seguirá caer en las siguientes dificultades?
La copia del código es la siguiente:
función test () {
alerta ("1");
}
prueba();
función test () {
alerta ("2");
}
prueba();
Al ejecutar el fragmento de código anterior, las ventanas emergentes que ves son "2", ¿por qué no son "1" y "2" respectivamente? Es muy simple, la declaración de prueba se analiza antes de la prueba (). Dado que este último anula el primero, el resultado de ambas ejecuciones es "2".
Complete el PIT: en la mayoría de los casos, uso expresiones de funciones en lugar de declaraciones de funciones, especialmente en algunos bloques de declaración.
4. Expresiones de funciones
Veamos primero la expresión de la función nombrada. Por supuesto, debe tener un nombre, por ejemplo:
Copie el código de la siguiente manera: var bar = function foo () {
// cuerpo...
};
Cabe señalar que el nombre de la función solo es visible para su función en el interior. Como las siguientes trampas:
La copia del código es la siguiente:
var bar = function foo () {
foo (); // ejecutar normalmente
};
foo (); // Error: referenceError
Complete el PIT: intente usar expresiones de función nombradas lo menos posible (a excepción de algunos fines de recursión y depuración), y nunca use los nombres de funciones externamente.
5. Funcionar autoexecución
Para las expresiones de funciones, puede ejecutar agregando () después, y los parámetros se pueden pasar entre paréntesis, mientras que la declaración de función no puede. fosa:
La copia del código es la siguiente:
// (1) ¡Esto es solo un operador de agrupación, no una llamada de función!
// Entonces la función aquí no se ha ejecutado, sigue siendo una declaración
función foo (x) {
alerta (x);
} (1);
Los siguientes fragmentos de código se ejecutan por separado, y la ventana emergente muestra "1". Porque antes (1), son expresiones de funciones, por lo que () aquí no es un operador de agrupación, sino un operador, que indica la ejecución de llamadas.
Copie el código de la siguiente manera: // Expresión de función anónima estándar
var bar = function foo (x) {
alerta (x);
} (1);
// el anterior () convierte la declaración de función en una expresión
(función foo (x) {
alerta (x);
}) (1);
// todo () es una expresión
(función foo (x) {
alerta (x);
} (1));
// nueva expresión
nueva función foo (x) {
alerta (x);
} (1);
// &&, || ,!, +, -, ~ etc. Operadores (y comas) para desambiguar expresiones de funciones y declaraciones de funciones
// Entonces, una vez que el analizador sabe que uno de ellos es una expresión, los otros también se incumplirán las expresiones.
verdadero && function foo (x) {
alerta (x);
} (1);
Llene el pozo: la clave de este pozo es descubrir la esencia de todo tipo de expresiones de funciones.
6. Cierre en el bucle
Lo siguiente demuestra una trampa común:
La copia del código es la siguiente:
<! Doctype html>
<html lang = "en">
<Evista>
<meta charset = "utf-8">
<title> documento </title>
</ablo>
<Body>
<H3> Al hacer clic en los enlaces a continuación, muestre el número de su secuencia </h3>
<ul>
<li> <a href = " #"> enlace #0 </a> </li>
<li> <a href = " #"> enlace #1 </a> </li>
<li> <a href = " #"> enlace #2 </a> </li>
<li> <a href = " #"> enlace #3 </a> </li>
<li> <a href = " #"> enlace #4 </a> </li>
</ul>
</body>
</html>
La copia del código es la siguiente:
Var enlaces = document.getElementsBytagName ("ul") [0] .getElementsBytagName ("A");
para (var i = 0, l = links.length; i <l; i ++) {
enlaces [i] .Onclick = function (e) {
E.PreventDefault ();
alerta ("Usted hace clic en el enlace #" + i);
}
}
Esperamos que al hacer clic en el enlace I-Th, obtengamos el valor del índice I en esta secuencia. Sin embargo, no importa en qué enlace se haga clic, obtenemos el resultado final de I después del bucle: "5".
Explique el motivo: cuando se llama alerta, la expresión de la función anónima en el bucle for para el bucle mantiene una referencia a la variable externa I (cierre). En este momento, el bucle ha terminado y el valor de I se modifica a "5".
Complete el pozo: para obtener el resultado deseado, debe crear una copia de la variable I en cada bucle. Lo siguiente demuestra el enfoque correcto:
La copia del código es la siguiente:
<Evista>
<meta charset = "utf-8">
<title> documento </title>
</ablo>
<Body>
<H3> Al hacer clic en los enlaces a continuación, muestre el número de su secuencia </h3>
<ul>
<li> <a href = " #"> enlace #0 </a> </li>
<li> <a href = " #"> enlace #1 </a> </li>
<li> <a href = " #"> enlace #2 </a> </li>
<li> <a href = " #"> enlace #3 </a> </li>
<li> <a href = " #"> enlace #4 </a> </li>
</ul>
</body>
</html>
La copia del código es la siguiente:
Var enlaces = document.getElementsBytagName ("ul") [0] .getElementsBytagName ("A");
para (var i = 0, l = links.length; i <l; i ++) {
enlaces [i] .Onclick = (function (index) {
función de retorno (e) {
E.PreventDefault ();
alerta ("Usted hace clic en el enlace #" + índice);
}
})(i);
}
Como puede ver, la forma de (function () {...}) () es la autoexecución de la función mencionada anteriormente. Me pasan al índice como parámetro. Cuando la alerta se ejecuta nuevamente, tiene una referencia al índice. En este momento, este valor no será cambiado por el bucle. Por supuesto, después de comprender el principio, también puede escribir así:
La copia del código es la siguiente:
para (var i = 0, l = links.length; i <l; i ++) {
(función (índice) {
enlaces [i] .Onclick = function (e) {
E.PreventDefault ();
alerta ("Usted hace clic en el enlace #" + índice);
}
})(i);
}
También funciona.