¿Existen usos legítimos para la declaración "with" de JavaScript?
Los comentarios de Alan Storm en respuesta a mi respuesta sobre la with
declaració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?
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: let
ywith
Con la introducción de la let
declaració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 let
sintaxis de bloque introducida en versiones anteriores de los navegadores Mozilla, pero no ampliamente adoptada en otros lugares.
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.
Como indicaron mis comentarios anteriores, no creo que puedas usarlo with
de 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.name
ya estaba configurado, ahora lo estará Bob
. Si no se configuró, el global name
se inicializará o cambiará Bob
y el user
objeto permanecerá sin name
propiedad.
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 with
es uno de ellos.