que - ¿Qué patrón(s) de diseño aprovechan el comportamiento de elevación de JavaScript?
javascript pdf (9)
Aquí hay un uso para izar:
(function() {
var factorial = function(n) {
if(n == 0)
return 1;
return n * factorial(n - 1);
};
})();
Sin izar, eso no compilaría porque el factorial
no existiría aún dentro del literal de la función. Tendría que declarar la variable por separado o usar una función con nombre.
JavaScript también permite código como el siguiente:
var test = function(b) {
if(b) {
var value = 2;
} else {
var value = 5;
}
console.log(value);
};
Con el análisis de bloques, tendría que agregar otra línea para declarar el value
antes del if
.
Para ser justos, este código funciona debido al alcance de la función, no al izar. Y JavaScript podría haber tenido alcance de función sin elevación. Ruby maneja esto mejor: Ruby tiene alcance de método para las variables, pero las variables no existen hasta que las estableces:
def test(b)
# unlike JavaScript, value is not accessible here
if b
value = 2
else
value = 5
end
puts value
end
El excelente artículo de Ben Cherry explica el levantamiento en JavaScript de manera adecuada. Mi problema, sin embargo, es que no puedo concebir un caso de uso para este notorio perpetrador de confusión. Explique si existe un patrón de diseño que aproveche esta función de idioma.
En segundo lugar, ¿el alcance es exclusivo de JavaScript?
ACTUALIZACIÓN --- Estoy agregando una recompensa por una respuesta que satisfaga mi curiosidad: ¿Qué patrón de diseño realmente aprovecha el comportamiento de elevación de JavaScript? Entiendo por qué JavaScript admite el levantamiento, pero quiero saber cómo puedo aprovechar esta característica .
Creo que un área donde el izado es útil se debe al hecho de que las funciones se tratan como objetos de primera clase. Por ejemplo:
function foo()
{
function a()
{
//...
}
function b()
{
//...
}
}
también se puede escribir como:
function foo()
{
var a = function ()
{
//...
}
var b = function ()
{
//...
}
}
Sin izar, lo siguiente daría lugar a un error:
function foo()
{
var a = function ()
{
b();
}
a(); //Error in function since b is not yet defined
var b = function ()
{
//...
}
}
Supongo que solo podrían haber levantado objetos funcionales, pero creo que eso sería inconsistente con la filosofía de que las funciones deberían ser tratadas como ciudadanos de primera clase en el lenguaje.
JavaScript no tiene ámbito de bloque (olvidemos por ahora) y, por lo tanto, cualquier declaración de variable se declara para toda la función, cuyo JavaScript sí tiene alcance.
Si lo piensas de esa manera, el levantamiento de JavaScript puede tener más sentido.
Si recuerda acerca de izar, no debe ser una fuente de errores y confusión. Es simplemente una de esas peculiaridades que debes comprender y recordar.
No estoy seguro si el izado está limitado a JavaScript. Nunca he oído hablar de eso en otros lugares, pero eso no significa necesariamente que no exista en otros idiomas.
Los primeros dos ejemplos en ese artículo están mal escritos. El código incorrecto obviamente conduce a errores y confusión. Déjame darte las versiones modificadas de estos ejemplos. Verás que no hay confusión aquí ...
Ejemplo 1 - Código original
var foo = 1;
function bar() {
if (!foo) {
var foo = 10;
}
alert(foo);
}
bar();
Ejemplo 1 - Código refactorizado (se eliminó la confusión)
var foo = 1;
function bar() {
var foo;
if ( !foo ) {
foo = 10;
}
alert( foo );
}
bar();
La alerta muestra "10" y está claro por qué. No hay confusión aquí.
Ejemplo 2 - Código original
var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
alert(a);
Ejemplo 2 - Código refactorizado (se eliminó la confusión)
var a = 1;
function b() {
var a = function () {};
a = 10;
return;
}
b();
alert( a );
La alerta muestra "1". Obviamente. No hay confusión aquí, también.
Elevación variable
Uno de los usos más simples del izado es el izado variable. Si no tuviéramos elevación variable, esto arrojaría un error de ReferenceError
:
var bar = foo;
var foo;
Eso no parece inmediatamente útil, pero nos permite hacer cosas como esta:
var myCoolJS = myCoolJS || {};
Esto básicamente significa lo que parece: myCoolJS
es myCoolJS
si existe, o un nuevo objeto si no lo es. El segundo myCoolJS
no lanza un ReferenceError
si myCoolJS
no existía ya que esta declaración de variable es izada.
Esto nos evita hacer un tipo incómodo de typeof myCoolJS != ''undefined''
.
Elevación de función
El izado de funciones puede ser especialmente útil cuando se combinan varios scripts en uno . Por ejemplo, he creado una implementación ligera de tiempo de compilación de módulos CommonJS . Esto proporciona el mismo module
, require
y exports
características que se encuentran en node.js. Construí la herramienta para permitir que los módulos requeridos se compongan de múltiples archivos. Por ejemplo, require(''/foo'')
podría dar como resultado un módulo compuesto por dos archivos, foo.js
(el "archivo de cuerpo") y foo.h.js
(el "archivo de encabezado").
Esto permite que el "archivo de cuerpo" no tenga conocimiento de las variables gratuitas proporcionadas por el entorno de los módulos de CommonJS; todo eso se maneja en el encabezado. Esto hace que el código sea reutilizable y fácil de probar sin construir. Sin embargo, dado que los encabezados se anteponen al cuerpo, aprovechamos la función de elevación en el archivo de cuerpo para permitir las exportaciones en los encabezados. Por ejemplo:
// dom.h.js
var util = require(''util'').util;
exports.css = css; // we can do this because "css" is hoisted from below
// ... other exports ...
...
// dom.js
function css(){}; // this would normally just be an object.
css.hasClass = function(element) { ... };
css.addClass = function(element) { ... };
// ...other code...
"izar" no es parte del estándar ECMAScript, pero sí dice que la variable dentro de una función se declara al comienzo de la función independientemente de dónde en la función se coloca en el código.
Ejemplo
(function() {
alert(myvar); // undefined
var myvar = ''local value'';
})();
Internamente, Javascript declararía myvar antes de la alerta, mostraría la alerta, y luego asignaría myvar a ''valor local''.
Por lo tanto, Javascript interpetiría ese código como:
(function() {
var myvar;
alert(myvar); // undefined
myvar = ''local value'';
})();
Es por eso que "partes de Java the Good" tiene una directriz que dice que debe declarar variables en la parte superior de su función.
Fuente: http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-hoisting-explained/
"Explique si hay un patrón de diseño que aproveche esta función de idioma". "izar" no es una característica, sino una consecuencia de cómo el intérprete de JavaScript estructura el código, ya que el lenguaje utiliza el ámbito de función.
"¿Qué patrón (s) de diseño en realidad aprovechan el comportamiento de elevación de JavaScript?" Respuesta: ninguna.
Me gusta el estilo de la pregunta, basado en la curiosidad sobre el idioma. Obviamente, nadie debería usar la función de izar como una característica, a menos que estén absolutamente seguros de que la dirección de su casa no puede ser descubierta por quienes puedan usarla más adelante.
Solo puedo imaginar algunos casos triviales. La propiedad básica para explotar es que la variable puede declararse (pero no definirse) y luego asignarse en una sola línea de código, pero con los eventos interpretados en dos puntos diferentes distintos.
Con la declaración al final de un bucle (no. Para cada uno como, por supuesto, establece el alcance) puede usar esto para detectar la primera iteración.
var notfirst = true; // this is actually never referenced.
(function () {
var number, stack = [1, 2, 3, 4, 5];
while (number = stack.pop()) {
if (notfirst) console.log(number);
var notfirst = true;
}
})();
La salida de vaciar la pila es 4, 3, 2, 1. 5 es rechazada.
De nuevo. No hagas esto!
Este es un caso de uso real (aunque reducido a pseudocódigo) para usted, de alguien que realmente quiere usar los beneficios de izar en libertad.
Recientemente escribí este script para manejar la validación y presentación de formularios simples. En la medida de lo posible, cada declaración de función invoca lo siguiente. Esto tiene 2 beneficios principales para la legibilidad:
- Secuencia lógica : hay un flujo secuencial al código, lo que significa que las funciones siempre se invocarán antes de que se declaren. Este es un beneficio porque, cuando se usa con funciones de baja complejidad, mantiene las cosas relativamente planas y le da una idea del contexto de llamada de una función poco antes de su origen. Solo tendrá que desplazarse hacia abajo (nunca hacia arriba) para seguir el código y, en la medida de lo posible, desplazarse en absoluto o muy poco.
- Bajo costo de referencia : me gusta mantener todas mis declaraciones de variables en la parte superior de cada alcance para que los lectores conozcan todas las partes móviles que requiere la función antes de leer su cuerpo, pero nadie quiere leer el código fuente de cada función invocada para tener una idea de lo que hace el alcance actual. Con este método, nunca se encontrará con una referencia de función antes de su declaración. Suena estúpido al principio, pero en realidad reduce la carga cognitiva: nunca se le da una fuente de función con lo implícito. Recuerde esto - lo usaremos más tarde - en su lugar, solo lee la fuente de la función una vez que conoce el contexto al que se llama en.
$( function emailHandler(){ var $form = … var $email = … var $feedback = … var value = … var validation = … // All initialisation is right here. Executes immediately. // Therefore, all future code will only ever be invoked // by call stacks passing through here. void function bindEvents(){ $email.on( ''input'', filterInput ); $form.on( ''submit'', preSubmit ); }(); function filterInput( inputEvent ){ if( inputEvent && inputEvent.which === ''13'' ){ return presubmit( inputEvent ); } return validate(); } function validate(){ var presentValue = $email.val(); if( validation.done || presentValue === value ){ return; } else if( presentValue === placeholder || presentValue === '''' ){ validation.message = ''You must enter an email address to sign up''; validation.valid = false; } else if( !validation.pattern.test( presentValue ) ){ validation.message = ''Please enter a valid email address''; validation.valid = false; } else { validation.message = ''''; validation.valid = true; } validation.ever = true; clearFeedback(); } function preSubmit( inputEvent ){ if( inputEvent instanceof $.Event ){ inputEvent.preventDefault(); } if( !validation.ever ){ validate(); } if( validation.pending || validation.done ){ return; } else if( validation.valid ){ return submit(); } else { return feedback(); } } function submit(){ $form.addClass( ''pending'' ); // Create an XHR base on form attributes $.ajax( { cache : false, url : $form.attr( ''action'' ), data : $form.serialize(), type : $form.attr( ''method'' ).toUpperCase() } ) .done( success ) .fail( failure ) .always( feedback ); } function success( response ){ validation.message = response.message; validation.valid = response.valid; } function failure(){ validation.message = ''Our server couldn/'t sign you up. Please try again later.''; validation.error = true; } function feedback(){ clearFeedback(); if( validation.message ){ $feedback .html( validation.message ) .appendTo( $placeholder ); } if( !validation.valid || validation.error ){ $form.addClass( ''invalid'' ); $email.trigger( ''focus'' ); } else { $form.addClass( ''done'' ); validation.done = true; } validation.pending = false; } function clearFeedback(){ $form.removeClass( ''invalid pending done'' ); } } );
Si considera la forma en que se escriben otros idiomas (C ++ / Java) y cómo se usan sus patrones de clase, se podría aprovechar el alzamiento para escribir un patrón similar para construir prototipos.