Acabo de leer un gran artículo sobre JavaScript Scoping and Hoisting de Ben Cherry en el que da el siguiente ejemplo:
var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
alert(a);
Usando el código anterior, el navegador alertará a "1".
Todavía no estoy seguro de por qué devuelve "1". Algunas de las cosas que dice vienen a la mente como: Todas las declaraciones de funciones están subidas a la parte superior. Puede medir una variable usando function. Todavía no me hace clic.
javascript
scope
scoping
hoisting
desarrollador
fuente
fuente
var a
debe estar antes de la declaración de función y la asignacióna = 1
debe estar después. Pero tenga en cuenta que el analizador, el tokenizador, el intérprete, el compilador, lo que sea, no especifica que esto suceda realmente , es solo un equivalente.function a() {}
comportó igual quevar a = function () {};
" - esto es incorrecto de dos maneras: primero, en todo caso, habría sidovar a = function a() {};
(la función en realidad no es anónima), segundo, esas dos formas no son intercambiables, porque devar a = function a() {};
sólo lavar a;
parte habría sido izada. Laa = function a() {};
parte aún estaría detrás de la declaración de devolución. Debido a que la forma original es una declaración de función y no una expresión de función, en realidad se eleva como un todo.Lo que debe recordar es que analiza toda la función y resuelve todas las declaraciones de variables antes de ejecutarla. Entonces....
function a() {}
realmente se convierte en
var a = function () {}
var a
la fuerza a un ámbito local, y el ámbito de la variable es a través de toda la función, por lo que la variable a global sigue siendo 1 porque ha declarado a en un ámbito local convirtiéndola en una función.fuente
La función
a
se eleva dentro de la funciónb
:var a = 1; function b() { function a() {} a = 10; return; } b(); alert(a);
que es casi como usar
var
:var a = 1; function b() { var a = function () {}; a = 10; return; } b(); alert(a);
La función se declara localmente y la configuración
a
solo ocurre en el ámbito local, no en la var global.fuente
function a(){}
se eleva primero y se comporta comovar a = function () {};
, por lo tanto,a
se crea el alcance local .a=10
, está configurando la variable locala
, no la global.Por lo tanto, el valor de la variable global sigue siendo el mismo y recibe una alerta 1
fuente
function a() { }
es una declaración de función, que crea unaa
variable local a lab
función.Las variables se crean cuando se analiza una función, independientemente de si
var
se ejecuta la instrucción de la función o.a = 10
establece esta variable local.fuente
a = 10
establece una variable en el ámbito global cuandob
se ejecuta la función, a menos que usted agregue"use strict"
(en entornos como el soporte de esa directiva).¿Cuál es la manzana de la discordia en este pequeño fragmento de código?
Caso 1:
Incluya la
function a(){}
definición dentro del cuerpo de lafunction b
siguiente manera.logs value of a = 1
var a = 1; function b() { a = 10; return; function a() {} } b(); console.log(a); // logs a = 1
Caso 2
Excluya la
function a(){}
definición dentro del cuerpo defunction b
lo siguiente.logs value of a = 10
var a = 1; function b() { a = 10; // overwrites the value of global 'var a' return; } b(); console.log(a); // logs a = 10
La observación le ayudará a darse cuenta de que la declaración
console.log(a)
registra los siguientes valores.Caso 1: a = 1
Caso 2: a = 10
Posiciones
var a
ha sido definido y declarado léxicamente en el ámbito global.a=10
Esta declaración está reasignando valor a 10, léxicamente se encuentra dentro de la función b.Explicación de ambos casos
Debido a que
function definition with name property
a es igual quevariable a
. Elvariable a
interior sefunction body b
convierte en una variable local. La línea anterior implica que el valor global de a permanece intacto y el valor local de a se actualiza a 10.Entonces, lo que pretendemos decir es que el siguiente código
var a = 1; function b() { a = 10; return; function a() {} } b(); console.log(a); // logs a = 1
El intérprete de JS lo interpreta de la siguiente manera.
var a = 1; function b() { function a() {} a = 10; return; } b(); console.log(a); // logs a = 1
Sin embargo, cuando eliminamos el
function a(){} definition
, elvalue of 'a'
declarado y definido fuera de la función b, ese valor se sobrescribe y cambia a 10 en el caso 2. El valor se sobrescribe porque sea=10
refiere a la declaración global y si fuera a declararse localmente, debemos tener escritovar a = 10;
.var a = 1; function b() { var a = 10; // here var a is declared and defined locally because it uses a var keyword. return; } b(); console.log(a); // logs a = 1
Podemos aclarar nuestras dudas aún más por el cambio de la
name property
defunction a(){} definition
a algún otro nombre de'a'
var a = 1; function b() { a = 10; // here var a is declared and defined locally because it uses a var keyword. return; function foo() {} } b(); console.log(a); // logs a = 1
fuente
Izar es un concepto creado para nosotros para que sea más fácil de entender. Lo que realmente sucede es que las declaraciones se hacen primero con respecto a sus alcances y las asignaciones ocurrirán después de eso (no al mismo tiempo).
Cuando ocurren las declaraciones
var a
, entoncesfunction b
y dentro de eseb
alcance,function a
se declara.Esta función sombreará la variable a procedente del ámbito global.
Después de las declaraciones se hacen, los valores de asignación se iniciará, lo global
a
se obtendrá el valor1
y el interior de unafunction b
obtendrá10
. cuando lo hagaalert(a)
, llamará a la variable de alcance global real. Este pequeño cambio en el código lo hará más clarovar a = 1; function b() { a = 10; return a; function a() { } } alert(b()); alert(a);
fuente
Sorprendentemente, ninguna de las respuestas aquí menciona la relevancia del contexto de ejecución en la cadena de alcance.
El motor de JavaScript envuelve el código que se está ejecutando actualmente en un contexto de ejecución. El contexto de ejecución base es el contexto de ejecución global. Cada vez que se invoca una nueva función, se crea un nuevo contexto de ejecución y se coloca en la pila de ejecución. Piense en un Stack Frame sentado en una Invocation Stack en otros lenguajes de programación. Último en entrar primero en salir. Ahora, cada contexto de ejecución tiene su propio entorno variable y entorno externo en JavaScript.
Usaré el siguiente ejemplo como demostración.
1) Primero, ingresamos a la Fase de Creación del Contexto de Ejecución global. Se crean tanto el entorno exterior como el entorno variable del entorno léxico. El Objeto Global se configura y se coloca en la memoria con la variable especial 'this' apuntándolo. La función ay su código y la variable myVar con un valor indefinido se colocan en la memoria del entorno variable global. es importante tener en cuenta que el código de la función a no se ejecuta. Simplemente se coloca en la memoria con la función a.
2) En segundo lugar, es la fase de ejecución del contexto de ejecución. myVar ya no es un valor indefinido. Se inicializa con el valor 1, que se almacena en el entorno variable global. Se invoca la función a y se crea un nuevo contexto de ejecución.
3) En el contexto de ejecución de la función a, pasa por la fase de creación y ejecución de su propio contexto de ejecución. Tiene su propio entorno exterior y entorno variable, por lo tanto, su propio entorno léxico. La función by la variable myVar se almacenan en su entorno variable. Este entorno variable es distinto del entorno variable global. Dado que la función a se encuentra léxica (físicamente en el código) en el mismo nivel que el contexto de ejecución global, su entorno externo es el contexto de ejecución global. Por lo tanto, si la función a se refería a una variable que no está en su Entorno de Variables, buscará la Cadena de Alcance e intentará encontrar la variable en el Entorno de Variables del Contexto de Ejecución global.
4) La función b se invoca en la función a. Se crea un nuevo contexto de ejecución. Dado que se encuentra léxicamente en la función a, su entorno externo es a. Entonces, cuando hace referencia a myVar, dado que myVar no está en el entorno variable de la función b, buscará en el entorno variable de la función a. Lo encuentra allí y console.log imprime 2. Pero si la variable no estaba en el Entorno variable de la función a, entonces dado que el Entorno externo de la función a es el Contexto de ejecución global, la Cadena de alcance continuará buscando allí.
5) Una vez finalizada la ejecución de las funciones by a, se extraen de la pila de ejecución. El motor JavaScript de un solo subproceso continúa la ejecución en el contexto de ejecución global. Invoca la función b. Pero no hay función b en el entorno variable global y no hay otro entorno exterior para buscar en el contexto de ejecución global. Por lo tanto, el motor JavaScript genera una excepción.
function a(){ function b(){ console.log(myVar); } var myVar = 2; b(); } var myVar = 1; a(); b(); > 2 > Uncaught ReferenceError: b is not defined
El siguiente ejemplo muestra la cadena de alcance en acción. En el entorno variable del contexto de ejecución de la función b, no hay myVar. Entonces busca su entorno exterior, que es la función a. La función a tampoco tiene myVar en su entorno variable. Entonces, el motor busca el entorno externo de la función a, que es el entorno externo del contexto de ejecución global y myVar se define allí. Por lo tanto, console.log imprime 1.
function a(){ function b(){ console.log(myVar); } b(); } var myVar = 1; a(); > 1
Con respecto al contexto de ejecución y el entorno léxico asociado con él, incluido el entorno externo y el entorno variable, habilite el alcance de las variables en JavaScript. Incluso si invoca la misma función varias veces, para cada invocación, creará su propio contexto de ejecución. Entonces, cada Contexto de Ejecución tendrá su propia copia de las variables en su Entorno de Variables. No se comparten las variables.
fuente
Está sucediendo porque el nombre de la variable es el mismo que el nombre de la función significa "a". Por lo tanto, debido a la elevación de Javascript, intenta resolver el conflicto de nombres y devolverá a = 1.
También estaba confundido acerca de esto hasta que leí esta publicación sobre "JavaScript Hoisting" http://www.ufthelp.com/2014/11/JavaScript-Hoisting.html
Espero eso ayude.
fuente
Aquí está mi resumen de la respuesta con más anotaciones y un violín acompañante para jugar.
// hoisting_example.js // top of scope ie. global var a = 1 var a = 1; // new scope due to js' functional (not block) level scope function b() { a = 10; // if the function 'a' didn't exist in this scope, global a = 10 return; // the return illustrates that function 'a' is hoisted to top function a(){}; // 'a' will be hoisted to top as var a = function(){}; } // exec 'b' and you would expect to see a = 10 in subsequent alert // but the interpreter acutally 'hoisted' the function 'a' within 'b' // and in doing so, created a new named variable 'a' // which is a function within b's scope b(); // a will alert 1, see comment above alert(a);
https://jsfiddle.net/adjavaherian/fffpxjx7/
fuente
scpope y cierre y elevación (var / función)
var a = 1; //"a" is global scope function b() { var a = function () {}; //"a" is local scope var x = 12; //"x" is local scope a = 10; //global variable "a" was overwrited by the local variable "a" console.log("local a =" + a); return console.log("local x = " + x); } b(); // local a =10 // local x = 12 console.log("global a = " + a); // global a = 1 console.log("can't access local x = \n"); // can't access local x = console.log(x); // ReferenceError: x is not defined
fuente
Elevación En JavaScript significa que las declaraciones de variables se ejecutan en todo el programa antes de que se ejecute cualquier código. Por lo tanto, declarar una variable en cualquier parte del código equivale a declararla al principio.
fuente
Todo depende del alcance de la variable 'a'. Déjame explicarte creando ámbitos como imágenes.
Aquí JavaScript creará 3 ámbitos.
i) Alcance global. ii) Alcance de la función b (). iii) Función a () alcance.
Está claro cuando llama al alcance del método 'alerta' que pertenece a Global en ese momento, por lo que seleccionará el valor de la variable 'a' del alcance global solo que sea 1.
fuente
¡Mensaje largo!
¡Pero aclarará el aire!
La forma en que funciona Java Script es que implica un proceso de dos pasos:
Compilación (por así decirlo): este paso registra variables y declaraciones de funciones y su respectivo alcance. No implica evaluar la expresión de función:
var a = function(){}
o expresión de variable (como asignar3
ax
en caso devar x =3;
que no sea más que la evaluación de la parte RHS).Intérprete: Esta es la parte de ejecución / evaluación.
Verifique el resultado del siguiente código para comprenderlo:
//b() can be called here! //c() cannot be called. console.log("a is " + a); console.log("b is " + b); console.log("c is " + c); var a = 1; console.log("Now, a is " + a); var c = function() {}; console.log("Now c is " + c); function b() { //cannot write the below line: //console.log(e); //since e is not declared. e = 10; //Java script interpreter after traversing from this function scope chain to global scope, is unable to find this variable and eventually initialises it with value 10 in global scope. console.log("e is " + e) // works! console.log("f is " + f); var f = 7; console.log("Now f is " + f); console.log("d is " + d); return; function d() {} } b(); console.log(a);
Vamos a romperlo:
En la fase de compilación, se registraría 'a' en alcance global con valor '
undefined
'. Lo mismo ocurre con "c
", su valor en este momento seríaundefined
"y no elfunction()
". 'b
' se registraría como una función en el ámbito global. Dentrob
del alcance,f
se registraría como una variable indefinida en ese momento yd
se registraría la función.Cuando se ejecuta el intérprete,
function()
se puede acceder a las variables declaradas y (no a las expresiones) antes de que el intérprete alcance la línea de expresión real. Entonces, las variables se imprimirían 'undefined
' y la función anónima declarada se puede llamar antes. Sin embargo, intentar acceder a la variable no declarada antes de la inicialización de su expresión daría como resultado un error como:console.log(e) e = 3;
Ahora, ¿qué sucede cuando tienes una declaración de variable y función con el mismo nombre?
La respuesta es : las funciones siempre se levantan antes y si se declara la misma variable de nombre, se trata como duplicada y se ignora. Recuerde, el orden no importa. Las funciones siempre tienen prioridad. Pero durante la fase de evaluación, puede cambiar la referencia de la variable a cualquier cosa (almacena lo que fue la última asignación) Eche un vistazo al siguiente código:
var a = 1; console.log("a is " + a); function b() { console.log("a inside the function b is " + a); //interpreter finds 'a' as function() in current scope. No need to go outside the scope to find 'a'. a = 3; //a changed console.log("Now a is " + a); return; function a() {} } var a; //treated as duplicate and ignored. b(); console.log("a is still " + a + " in global scope"); //This is global scope a.
fuente
Izar es un concepto de comportamiento de JavaScript. Izar (digamos mover) es un concepto que explica cómo y dónde se deben declarar las variables.
En JavaScript, una variable se puede declarar después de que se haya utilizado porque las declaraciones de función y las declaraciones de variable siempre se mueven ("levantan") de manera invisible a la parte superior de su alcance contenedor por el intérprete de JavaScript.
Encontramos dos tipos de izado en la mayoría de los casos.
1.Alzado de declaración variable
Entendamos esto por este fragmento de código.
a = 5; // Assign 5 to a elem = document.getElementById("demo"); // Find an element elem.innerHTML = a; // Display a in the element var a; // Declare a //output-> 5
Aquí la declaración de la variable a será alojada en la parte superior de forma invisible por el intérprete de JavaScript en el momento de la compilación. Así que pudimos obtener el valor de un. Pero este enfoque de declaración de variables no se recomienda ya que deberíamos declarar variables al principio así.
var a = 5; // Assign and declare 5 to a elem = document.getElementById("demo"); // Find an element elem.innerHTML = a; // Display a in the element // output -> 5
considere otro ejemplo.
function foo() { console.log(x) var x = 1; }
en realidad se interpreta así:
function foo() { var x; console.log(x) x = 1; }
En este caso x no estará definido
No importa si se ha ejecutado el código que contiene la declaración de variable. Considere este ejemplo.
function foo() { if (false) { var a = 1; } return; var b = 1; }
Esta función resulta ser así.
function foo() { var a, b; if (false) { a = 1; } return; b = 1; }
En la declaración de variable, solo los polipastos de definición de variable, no la asignación.
A diferencia de la variable izar, también se izará el cuerpo de la función o el valor asignado. Considere este código
function demo() { foo(); // this will give error because it is variable hoisting bar(); // "this will run!" as it is function hoisting var foo = function () { alert("this would not run!!"); } function bar() { alert("this will run!!"); } } demo();
Ahora que entendimos tanto la elevación de variables como de funciones, entendamos este código ahora.
var a = 1; function b() { a = 10; return; function a() {} } b(); alert(a);
Este código resultará así.
var a = 1; //defines "a" in global scope function b() { var a = function () {}; //defines "a" in local scope a = 10; //overwrites local variable "a" return; } b(); alert(a);
La función a () tendrá alcance local dentro de b (). a () se moverá a la parte superior mientras se interpreta el código con su definición (solo en el caso de la función de elevación), por lo que ahora tendrá un alcance local y, por lo tanto, no afectará el alcance global de a mientras tiene su propio alcance dentro de la función b () .
fuente
Según mi conocimiento, el izado ocurre con la declaración de variable y la declaración de función, por ejemplo:
a = 7; var a; console.log(a)
Qué sucede dentro del motor de JavaScript:
var a; a = 7; console.log(a); // 7
O:
console.log(square(7)); // Output: 49 function square(n) { return n * n; }
Se convertirá:
function square(n) { return n * n; } console.log(square(7)); // 49
Pero las asignaciones como la asignación de variables, la asignación de expresión de función no se elevarán: Por ejemplo:
console.log(x); var x = 7; // undefined
Puede volverse así:
var x; console.log(x); // undefined x = 7;
fuente
Para describir el alojamiento en javascript en una oración, las variables y las funciones se colocan en la parte superior del alcance en el que se declaran.
Supongo que es un principiante, para comprender el izado correctamente al principio, hemos entendido la diferencia entre undefined y ReferenceError
var v; console.log(v); console.log(abc); /* The output of the above codes are: undefined ReferenceError: abc is not defined*/
ahora en el código de abajo lo que vemos? una variable y una expresión de función se descuenta.
<script> var totalAmo = 8; var getSum = function(a, b){ return a+b; } </script>
pero la imagen real con prueba de que tanto la variable como la función están en la parte superior de su alcance:
console.log(totalAmo); console.log(getSum(8,9)); var totalAmo = 8; var getSum = function(a, b){ return a+b; } console.log(totalAmo); console.log(getSum(9,7));
La salida de los dos primeros registros no está definida y TypeError: getSum no es una función porque tanto var totalAmo como getSum se colocan en la parte superior de su alcance como abajo
<script> var totalAmo; var getSum; console.log(totalAmo); console.log(getSum(8,9)); var totalAmo = 8; var getSum = function(a, b){ return a+b; } console.log(totalAmo); console.log(getSum(9,7)); </script>
Pero para la declaración de funciones, funciones completas se alzan en la parte superior de su alcance.
console.log(getId()); function getId(){ return 739373; } /* output: 739373, because the whole function hoisted on the top of the scope.*/
Ahora la misma lógica se aplica a esas variables, experesiones de funciones y declaratoins de funciones declarados dentro del alcance funcional. Punto clave: no se izarán en la parte superior del archivo ;
function functionScope(){ var totalAmo; var getSum; console.log(totalAmo); console.log(getSum(8,9)); var totalAmo = 8; var getSum = function(a, b){ return a+b; } }
Entonces, cuando usa la palabra clave var , variable y función en la parte superior de su alcance (alcance global y alcance de función). ¿Qué pasa con let y const? , const y let todavía son conscientes del alcance global y el alcance de la función al igual que var, pero las variables const y let también son conscientes de otro alcance llamado alcance bloqueado? un alcance de bloque está presente siempre que hay un bloque de código, como el bucle for, la declaración if else, el bucle while, etc.
Cuando usamos const y let para declarar una variable en el alcance de estos bloques, la declaración de la variable solo se elevará en la parte superior del bloque en el que se encuentra, y no se elevará en la parte superior de la función padre o en la parte superior de la alcance global que se iza.
function getTotal(){ let total=0; for(var i = 0; i<10; i++){ let valueToAdd = i; var multiplier = 2; total += valueToAdd*multiplier; } return total; }
Las variables en el ejemplo anterior se elevarán como abajo
function getTotal(){ let total; var multiplier; total = 0; for(var i = 0; i<10; i++){ let valueToAdd; valueToAdd = i; multiplier = 2; total += valueToAdd*multiplier; } return total; }
fuente
ES5: función de elevación y elevación variable
"use strict"; /** * * @author xgqfrms * @license MIT * @copyright xgqfrms * @created 2016-06-01 * @modified * * @description function-hoisting.js * @augments * @example * @link * */ (function() { const log = console.log; var a = 1; function b() { a = 10; log(`local a`, a) return; // function hoisting priority is greater than variable hoisting function a() {} } b(); log(`global a`, a); // local a 10 // global a 1 })();
que es igual a
(function() { const log = console.log; // define "a" in global scope var a = 1; function b() { // define "a" in local scope var a ; // assign function to a a = function () {}; // overwrites local variable "a" a = 10; log(`local a`, a); return; } b(); // log global variable "a" log(`global a`, a); // local a 10 // global a 1 })();
la razón detrás de izar
var a = 1; //"a" is global scope function b() { var a = function () {}; //"a" is local scope var x = 12; //"x" is local scope a = 10; //global variable "a" was overwrited by the local variable "a" console.log("local a =" + a); return console.log("local x = " + x); } b(); // local a =10 // local x = 12 console.log("global a = " + a); // global a = 1 console.log("can't access local x = \n"); // can't access local x = console.log(x); // ReferenceError: x is not defined /** * scpope & closure & hoisting (var/function) * * 1. scpope : the global var can be access in any place(the whole file scope), local var only can be accessed by the local scope(function/block scope)! * Note: if a local variable not using var keywords in a function, it will become a global variable! * * 2. closure : a function inner the other function, which can access local scope(parent function) & global scope, howerver it's vars can't be accessed by others! unless, your return it as return value! * * 3. hoisting : move all declare/undeclare vars/function to the scope top, than assign the value or null! * Note: it just move the declare, not move the value! * */
ES6
let
,const
no existe elevación(() => { const log = console.log; log(a) // Error: Uncaught ReferenceError: Cannot access 'a' before initialization let a = 1; })();
(() => { const log = console.log; log(b) // Error: Uncaught ReferenceError: Cannot access 'b' before initialization const b = 1; })();
refs
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
fuente