¿Cuál es el resultado del siguiente programa?
Copie el código de código de la siguiente manera:
varfoo = 1;
barra de funciones() {
si (!foo) {
varfoo = 10;
}
alerta(foo);
}
bar();
El resultado es 10;
¿Qué pasa con este?
Copie el código de código de la siguiente manera:
var a = 1;
función b() {
a = 10;
devolver;
función a() {}
}
b();
alerta(a);
El resultado es 1.
¿Te asusta? ¿Qué pasó? Esto puede resultar extraño, peligroso y confuso, pero en realidad también es una característica del lenguaje JavaScript muy útil e impresionante. No sé si existe un nombre estándar para este comportamiento, pero me gusta este término: "Elevación". Este artículo brindará una explicación introductoria de este mecanismo, pero primero comprendamos lo necesario sobre el alcance de JavaScript.
Alcance de Javascript
Para los principiantes en Javascript, una de las áreas más confusas es el alcance; de hecho, no se trata sólo de principiantes. He conocido a algunos programadores de JavaScript experimentados, pero no comprenden profundamente el alcance. La razón por la que el alcance de JavaScript es confuso es porque la sintaxis del programa en sí parece un lenguaje de la familia C, como el siguiente programa C:
Copie el código de código de la siguiente manera:
#incluir <stdio.h>
int principal() {
entero x = 1;
printf("%d, ", x); // 1
si (1) {
entero x = 2;
printf("%d, ", x); // 2
}
printf("%d/n", x); // 1
}
El resultado de salida es 1 2 1. Esto se debe a que los lenguajes de la familia C tienen alcance de bloque. Cuando el control del programa ingresa a un bloque, como un bloque if, las variables que solo afectan el bloque se pueden declarar sin afectar los efectos fuera del bloque. dominio. Pero en Javascript esto no funciona. Eche un vistazo al código a continuación:
Copie el código de código de la siguiente manera:
var x = 1;
consola.log(x); // 1
si (verdadero) {
var x = 2;
consola.log(x); // 2
}
consola.log(x); // 2
El resultado será 1 2 2. Porque javascript es el alcance de la función. Ésta es la mayor diferencia con la familia de lenguajes C. El if en este programa no crea un nuevo alcance.
Para muchos programadores de C, C++ y Java, esto no es lo que esperan y no agradecen. Afortunadamente, debido a la flexibilidad de las funciones de JavaScript, existen formas de solucionar este problema. Si debe crear un alcance temporal, haga algo como esto:
Copie el código de código de la siguiente manera:
función foo() {
var x = 1;
si (x) {
(función () {
var x = 2;
// algún otro código
}());
}
// x sigue siendo 1.
}
Este método es flexible y se puede utilizar en cualquier lugar donde desee crear un alcance temporal. No sólo dentro del bloque. Sin embargo, le recomiendo encarecidamente que se tome el tiempo para comprender el alcance de JavaScript. Es muy útil y una de mis funciones favoritas de JavaScript. Si comprende el alcance, entonces la elevación variable tendrá más sentido para usted.
Declaración, denominación y promoción de variables
En JavaScript, hay 4 formas básicas para que las variables entren en el ámbito:
•1 Idioma incorporado: todos los ámbitos tienen esto y argumentos (Nota del traductor: después de la prueba, los argumentos no son visibles en el ámbito global)
•2 Parámetros formales: Los parámetros formales de una función serán parte del alcance del cuerpo de la función;
•3 Declaración de función: como esta: function foo(){};
•4 Declaración de variable: así: var foo;
Las declaraciones de funciones y las declaraciones de variables siempre son "elevadas" silenciosamente a la parte superior del cuerpo del método por parte del intérprete. Esto significa, código como el siguiente:
Copie el código de código de la siguiente manera:
función foo() {
bar();
var x = 1;
}
en realidad se interpretará como:
Copie el código de código de la siguiente manera:
función foo() {
varx;
bar();
x = 1;
}
Independientemente de si se puede ejecutar el bloque en el que está definida la variable. Las dos funciones siguientes son en realidad lo mismo:
Copie el código de código de la siguiente manera:
función foo() {
si (falso) {
var x = 1;
}
devolver;
var y = 1;
}
función foo() {
var x, y;
si (falso) {
x = 1;
}
devolver;
y = 1;
}
Tenga en cuenta que las asignaciones de variables no se elevan, solo declaraciones. Sin embargo, la declaración de función es un poco diferente y también se promueve el cuerpo de la función. Pero tenga en cuenta que hay dos formas de declarar una función:
Copie el código de código de la siguiente manera:
prueba de función() {
foo(); // TypeError "foo no es una función"
bar(); // "¡esto se ejecutará!"
var foo = function () { // la variable apunta a la expresión de la función
alert("¡esto no se ejecutará!");
}
function bar() { // Función de declaración de función denominada bar
alert("¡esto se ejecutará!");
}
}
prueba();
En este ejemplo, solo se elevan las declaraciones funcionales junto con el cuerpo de la función. Se levantará la declaración de foo, pero el cuerpo de la función al que apunta solo se asignará durante la ejecución.
Lo anterior cubre algunos de los conceptos básicos del impulso y no parecen tan confusos. Sin embargo, en algunos escenarios especiales, todavía existe un cierto grado de complejidad.
Orden de análisis de variables
Lo más importante a tener en cuenta es el orden de resolución variable. ¿Recuerda las 4 formas en que los nombres entran en el alcance que mencioné anteriormente? El orden en que se analizan las variables es el orden en que las enumeré.
Copie el código de código de la siguiente manera:
<guión>
función a(){
}
var a;
alert(a);//Imprime el cuerpo de la función de un
</script>
<guión>
var a;
función a(){
}
alert(a);//Imprime el cuerpo de la función de un
</script>
//Pero preste atención a la diferencia entre los dos métodos de escritura siguientes:
<guión>
var a = 1;
función a(){
}
alert(a);//Imprimir 1
</script>
<guión>
función a(){
}
var a = 1;
alert(a);//Imprimir 1
</script>
Hay 3 excepciones aquí:
1 Los argumentos de nombre integrados se comportan de manera extraña. Parece que deberían declararse después de los parámetros formales de la función, pero antes de la declaración de la función. Esto significa que si hay argumentos en el parámetro formal, tendrá prioridad sobre el integrado. Esta es una característica muy mala, así que evite usar argumentos en parámetros formales;
2 Definir esta variable en cualquier lugar provocará un error de sintaxis, lo cual es una buena característica;
3. Si varios parámetros formales tienen el mismo nombre, el último tiene prioridad, incluso si su valor no está definido durante la operación real;
función nombrada
Puedes darle un nombre a una función. Si es así, no es una declaración de función y el nombre de función especificado (si lo hay, como spam a continuación, nota del traductor) en la definición del cuerpo de la función no se promoverá, sino que se ignorará. Aquí hay un código para ayudarlo a comprender:
Copie el código de código de la siguiente manera:
foo(); // TypeError "foo no es una función"
barra(); // válido
baz(); // TypeError "baz no es una función"
spam(); // Error de referencia "el spam no está definido"
var foo = function () {}; // foo apunta a una función anónima
barra de funciones() {} // declaración de función
var baz = function spam() {}; // Función con nombre, solo se promociona baz, no se promoverá el spam.
foo(); // válido
barra(); // válido
baz(); // válido
spam(); // Error de referencia "el spam no está definido"
Cómo escribir código
Ahora que comprende el alcance y la elevación de variables, ¿qué significa esto para la codificación JavaScript? Lo más importante es definir siempre tus variables con var. Y recomiendo encarecidamente que, para un nombre, siempre haya una sola declaración var en un ámbito. Si hace esto, no tendrá problemas de alcance ni de elevación variable.
¿Qué quieres decir con especificación de idioma?
La documentación de referencia de ECMAScript siempre me resulta útil. Esto es lo que encontré sobre el alcance y la elevación variable:
Si una variable se declara en una clase de cuerpo de función, es el alcance de la función. De lo contrario, tiene un alcance global (como propiedad de global). Las variables se crearán cuando la ejecución entre en el alcance. Los bloques no definirán nuevos alcances, solo las declaraciones de funciones y los procedimientos (el traductor cree que es una ejecución de código global) crearán nuevos alcances. Las variables se inicializan en indefinido cuando se crean. Si hay una operación de asignación en la declaración de declaración de variable, la operación de asignación solo ocurrirá cuando se ejecute, no cuando se cree.
Espero que este artículo aporte un rayo de luz a los programadores que están confundidos acerca de JavaScript. También hago todo lo posible para evitar causar más confusión. Si dije algo mal o pasé por alto algo, hágamelo saber.
Suplemento del traductor
Un amigo me recordó el problema de promoción de funciones con nombre en el ámbito global en IE:
Así lo probé cuando traduje el artículo:
Copie el código de código de la siguiente manera:
<guión>
función(){
correo basura();
var baz = función spam() {alerta('esto es spam')};
}
t();
</script>
Esta forma de escribir, es decir, la promoción de funciones nombradas en un ámbito no global, tiene el mismo rendimiento en ie y ff. Lo cambié a:
Copie el código de código de la siguiente manera:
<guión>
correo basura();
var baz = función spam() {alerta('esto es spam')};
</script>
Entonces el spam se puede ejecutar bajo ie, pero no bajo ff. Esto muestra que diferentes navegadores manejan este detalle de manera diferente.
Esta pregunta también me llevó a pensar en otras dos preguntas: 1: Para las variables que tienen alcance global, existe una diferencia entre var y no var, sin var, la variable no se promocionará. Por ejemplo, de los dos programas siguientes, el segundo informará un error:
Copie el código de código de la siguiente manera:
<guión>
alerta(a);
var a = 1;
</script>
Copie el código de código de la siguiente manera:
<guión>
alerta(a);
a=1;
</script>
2: Las variables locales creadas en eval no se promocionarán (no hay forma de hacerlo).
Copie el código de código de la siguiente manera:
<guión>
var a = 1;
función t(){
alerta(a);
eval('var a = 2');
alerta(a);
}
t();
alerta(a);
</script>