¿Existen usos legítimos para la declaración "with" de JavaScript?

Resuelto Shog9 asked hace 16 años • 0 respuestas

Los comentarios de Alan Storm en respuesta a mi respuesta sobre la withdeclaración me hicieron pensar. Rara vez he encontrado una razón para usar esta característica particular del lenguaje y nunca había pensado mucho en cómo podría causar problemas. Ahora, tengo curiosidad por saber cómo podría hacer un uso eficaz de with, evitando al mismo tiempo sus riesgos.

¿ Dónde ha encontrado withútil la declaración?

Shog9 avatar Sep 15 '08 01:09 Shog9
Aceptado

Hoy se me ocurrió otro uso, así que busqué en la web con entusiasmo y encontré una mención existente: Definición de variables dentro del alcance del bloque .

Fondo

JavaScript, a pesar de su parecido superficial con C y C++, no limita las variables al bloque en el que están definidas:

var name = "Joe";
if ( true )
{
   var name = "Jack";
}
// name now contains "Jack"

Declarar un cierre en un bucle es una tarea común que puede generar errores:

for (var i=0; i<3; ++i)
{
   var num = i;
   setTimeout(function() { alert(num); }, 10);
}

Debido a que el bucle for no introduce un nuevo alcance, las tres funciones compartirán el mismo num(con un valor de ).2

Un nuevo alcance: letywith

Con la introducción de la letdeclaración en ES6 , resulta fácil introducir un nuevo alcance cuando sea necesario para evitar estos problemas:

// variables introduced in this statement 
// are scoped to each iteration of the loop
for (let i=0; i<3; ++i)
{
   setTimeout(function() { alert(i); }, 10);
}

O incluso:

for (var i=0; i<3; ++i)
{
   // variables introduced in this statement 
   // are scoped to the block containing it.
   let num = i;
   setTimeout(function() { alert(num); }, 10);
}

Hasta que ES6 esté disponible universalmente, este uso seguirá limitado a los navegadores y desarrolladores más nuevos que deseen utilizar transpiladores. Sin embargo, podemos simular fácilmente este comportamiento usando with:

for (var i=0; i<3; ++i)
{
   // object members introduced in this statement 
   // are scoped to the block following it.
   with ({num: i})
   {
      setTimeout(function() { alert(num); }, 10);
   }
}

El bucle ahora funciona según lo previsto, creando tres variables separadas con valores de 0 a 2. Tenga en cuenta que las variables declaradas dentro del bloque no tienen su ámbito, a diferencia del comportamiento de los bloques en C++ (en C, las variables deben declararse al comienzo de un bloque, por lo que en cierto modo es similar). En realidad, este comportamiento es bastante similar a una letsintaxis de bloque introducida en versiones anteriores de los navegadores Mozilla, pero no ampliamente adoptada en otros lugares.

Shog9 avatar Oct 08 '2008 23:10 Shog9

He estado usando la declaración with como una forma simple de importación con alcance. Digamos que tienes algún tipo de generador de marcado. En lugar de escribir:

markupbuilder.div(
  markupbuilder.p('Hi! I am a paragraph!',
    markupbuilder.span('I am a span inside a paragraph')
  )
)

En su lugar podrías escribir:

with(markupbuilder){
  div(
    p('Hi! I am a paragraph!',
      span('I am a span inside a paragraph')
    )
  )
}

Para este caso de uso, no estoy haciendo ninguna tarea, por lo que no tengo el problema de ambigüedad asociado con eso.

airportyh avatar Sep 22 '2009 19:09 airportyh

Como indicaron mis comentarios anteriores, no creo que puedas usarlo withde manera segura, sin importar cuán tentador pueda ser en una situación determinada. Como el tema no se trata directamente aquí, lo repetiré. Considere el siguiente código

user = {};
someFunctionThatDoesStuffToUser(user);
someOtherFunction(user);

with(user){
    name = 'Bob';
    age  = 20;
}

Sin investigar cuidadosamente esas llamadas a funciones, no hay forma de saber cuál será el estado de su programa después de que se ejecute este código. Si user.nameya estaba configurado, ahora lo estará Bob. Si no se configuró, el global namese inicializará o cambiará Boby el userobjeto permanecerá sin namepropiedad.

Los errores ocurren. Si lo usa, eventualmente lo hará y aumentará las posibilidades de que su programa falle. Peor aún, puede encontrar código de trabajo que establezca un global en el bloque with, ya sea deliberadamente o porque el autor no conoce esta peculiaridad de la construcción. Es muy parecido a encontrar una falla en un interruptor, no tienes idea si el autor pretendía esto y no hay manera de saber si "arreglar" el código introducirá una regresión.

Los lenguajes de programación modernos están repletos de funciones. Algunas características, después de años de uso, resultan ser malas y deben evitarse. El de Javascript withes uno de ellos.

Alana Storm avatar Sep 14 '2008 21:09 Alana Storm