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

369

Los comentarios de Alan Storm en respuesta a mi respuesta con respecto a la withdeclaración me hicieron pensar. Raramente he encontrado una razón para usar esta característica del lenguaje en particular, y nunca he pensado mucho en cómo podría causar problemas. Ahora, tengo curiosidad acerca de cómo podría hacer un uso efectivo with, evitando sus dificultades.

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

Shog9
fuente
52
Nunca lo uso Es más fácil vivir sin él si pretendo que no existe.
Nosredna
66
Es posible que alguna vez haya tenido muchos usos válidos. Pero es discutible. ES5 Strict eliminado, withpor lo que ya no existe tal cosa.
Thomas Aylott
27
Vale la pena señalar aquí que ES5 Strict sigue siendo opcional .
Shog9
55
En lugar de eliminar 'with' en ES5 estricto, ¿no habría sido mejor cambiar el estándar para que si no se encuentra ninguna variable, cualquier asignación realizada dentro de 'with' está vinculada al objeto de argumento?
JussiR
2
@JussiR: Probablemente. Pero el problema al hacerlo es que posiblemente rompería las cosas en los navegadores más antiguos.
Sune Rasmussen

Respuestas:

520

Se me ocurrió otro uso hoy, así que busqué en la web con entusiasmo y encontré una mención existente: Definición de variables dentro de Block Scope .

Antecedentes

JavaScript, a pesar de su parecido superficial con C y C ++, no abarca las variables del bloque en el que se definen:

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

Declarar un cierre en un ciclo es una tarea común en la que esto puede conducir a 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é universalmente disponible, este uso permanece limitado a los navegadores y desarrolladores más nuevos dispuestos a usar 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 ciclo 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 alcance, 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). Este comportamiento es en realidad bastante similar a una letsintaxis de bloque introducida en versiones anteriores de los navegadores Mozilla, pero no ampliamente adoptada en otros lugares.

Shog9
fuente
15
Nunca pensé en usar con un literal, parece legítimo.
Matt Kantor
81
Esto está realmente muy muerto. Nunca he pensado en jugar con el alcance de JavaScript de esta manera. Nuevas áreas totalmente expandidas a mi codificación. ¡Ojalá pudiera votar 10 veces!
kizzx2
27
Para aquellos que aún se oponen, uno siempre podría usar un cierre:for (var i = 0; i < 3; ++i) { setTimeout ((function () { var num = i; return function () { alert (num); }; }) (), 10);}
Thomas Eding
44
En realidad, el problema vinculado anteriormente aparece en la mayoría de los navegadores que no son de Mozilla (Chrome, Safari, Opera, IE).
Max Shawabkeh el
24
dejar que el apoyo comunicado en el IE sería realmente salvar el pellejo en este momento, estoy luchando con mi conciencia de si utilizar o no con el lugar. El verdadero problema es que incluso con un con como let , aún se debe tener especial cuidado debido a las propiedades heredadas de un objeto en la cadena del prototipo. Por ejemplo, var toString = function () { return "Hello"; }; with ({"test":1}) { console.log(toString()); };. En el ámbito de la instrucción with , toString () es una propiedad heredada de Object , por lo que no se llama a la función definida explícitamente. Sin embargo, sigue siendo una gran respuesta :-)
Andy E
161

He estado usando la declaración with como una forma simple de importación con ámbito. Digamos que tiene un generador de marcado de algún tipo. 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ía 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
fuente
55
Así es como lo he visto usado en VB. (Y el único uso que conocía.)
Mateen Ulhaq
2
Una desventaja de esto sería que si hace referencia a una variable dentro del bloque with que está fuera del objeto markupbuilder, el motor js primero lo buscará dentro de markupbuilder de todos modos, reduciendo el rendimiento.
Adam Thomas el
3
Esto realmente ayuda a reducir el código para aquellos que trabajan con rutas de lienzo.
Brian McCutchon
44
La versión "con" de ese código literalmente se ejecuta más de 240 veces más lento en mi máquina que la versión "no con" del mismo. Es por eso que la gente dice que no tiene un uso legítimo. No porque no pueda hacer que el código sea más bonito en algunos lugares. Ver punto de referencia: jsfiddle.net/sc46eeyn
Jimbo Jonny
1
@McBrainy: ese es exactamente el tipo de lugar en el que no deberías usar código que se ejecuta más lentamente (mira el comentario que acabo de hacer sobre este). Si necesita accesos directos para código súper repetido, puede declararlos. Por ejemplo, si usa context.bezierCurveTocien veces seguidas, puede decir var bc2 = context.bezierCurveTo;y luego ir bc2(x,x,etc);cada vez que quiera llamarlo. Eso es bastante rápido e incluso menos detallado, mientras que withes súper lento.
Jimbo Jonny
83

Como lo indicaron mis comentarios anteriores, no creo que pueda usarlo de withmanera segura, sin importar lo tentador que pueda ser en cualquier situación. Como el problema no está cubierto 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 de función, 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 estableció, el global namese inicializará o cambiará a Boby el userobjeto permanecerá sin una namepropiedad.

Los errores suceden. Si se utiliza con el que finalmente va a hacer esto y aumentar las posibilidades de su programa fallará. Peor aún, puede encontrar un código de trabajo que establece un global con el bloque with, ya sea deliberadamente o a través del autor, sin saber acerca de esta peculiaridad de la construcción. Es muy parecido a encontrarse con un interruptor, no tiene idea de si el autor lo intentó y no hay forma de saber si "arreglar" el código introducirá una regresión.

Los lenguajes de programación modernos están repletos de funciones. Se descubre que algunas características, después de años de uso, son malas y deben evitarse. Javascript withes uno de ellos.

Alan Storm
fuente
18
Este problema solo aparece cuando asigna valores al atributo del objeto. Pero, ¿qué pasa si lo está usando solo para leer valores? Sostengo que está bien usarlo en ese caso.
airportyh
10
El mismo problema se aplica a la lectura de los valores de Toby. En el fragmento de código anterior, no sabe si el nombre está configurado en el objeto de usuario, por lo que no sabría si estaba leyendo el nombre global o el nombre de usuario.
Alan Storm
12
Con los valores de lectura hay una regla de precedencia clara: los atributos en el objeto se verifican antes que las variables fuera del alcance. Esto no es diferente del alcance de las variables en las funciones. El verdadero problema con la asignación y 'con', según tengo entendido, radica en el hecho de que si la asignación del atributo ocurre o no depende de si el atributo existe en el objeto actual en cuestión, que es una propiedad de tiempo de ejecución y no se puede deducir fácilmente mirando el código
airportyh
1
Creo que puedes estar allí, Toby. El problema de escritura es suficiente para que evite la construcción por completo.
Alan Storm
"Es muy parecido a encontrarse con un interruptor, no tienes idea ..." - Entonces, ¿prohibimos también el interruptor ()? ;-p
Sz.
66

De hecho, la withdeclaración me pareció increíblemente útil recientemente. Esta técnica nunca se me ocurrió hasta que comencé mi proyecto actual: una consola de línea de comandos escrita en JavaScript. Estaba tratando de emular las API de la consola Firebug / WebKit donde se pueden ingresar comandos especiales en la consola pero no anulan ninguna variable en el ámbito global. Pensé en esto al tratar de superar un problema que mencioné en los comentarios a la excelente respuesta de Shog9 .

Para lograr este efecto, utilicé dos con declaraciones para "superponer" un alcance detrás del alcance global:

with (consoleCommands) {
    with (window) {
        eval(expression); 
    }
}

Lo mejor de esta técnica es que, aparte de las desventajas de rendimiento, no sufre los temores habituales de la withdeclaración, porque de todos modos estamos evaluando en el ámbito global: no hay peligro de que las variables fuera de nuestro pseudo-alcance sean modificado.

Me inspiró publicar esta respuesta cuando, para mi sorpresa, logré encontrar la misma técnica utilizada en otros lugares: ¡el código fuente de Chromium !

InjectedScript._evaluateOn = function(evalFunction, object, expression) {
    InjectedScript._ensureCommandLineAPIInstalled();
    // Surround the expression in with statements to inject our command line API so that
    // the window object properties still take more precedent than our API functions.
    expression = "with (window._inspectorCommandLineAPI) { with (window) { " + expression + " } }";
    return evalFunction.call(object, expression);
}

EDITAR: Acabo de verificar la fuente de Firebug, encadenan 4 con declaraciones juntas para incluso más capas. ¡Loco!

const evalScript = "with (__win__.__scope__.vars) { with (__win__.__scope__.api) { with (__win__.__scope__.userVars) { with (__win__) {" +
    "try {" +
        "__win__.__scope__.callback(eval(__win__.__scope__.expr));" +
    "} catch (exc) {" +
        "__win__.__scope__.callback(exc, true);" +
    "}" +
"}}}}";
Andy E
fuente
1
pero preocupado de que ecmascript5 te impida hacer esto. ¿Hay una solución ecmascript 5?
kybernetikos
@ Adam: no estoy seguro de eso. ES5 solo arroja un error para esto en modo estricto, por lo que no es un problema inmediato si no tiene un modo estricto declarado globalmente. ES Harmony podría plantear un problema mayor, pero podría solucionarse con algunas de las cosas más nuevas, como los proxies.
Andy E
@AndyE lo siento, esto está fuera de tema, pero ¿está disponible su 'consola de línea de comandos escrita en JavaScript' en alguna parte?
kybernetikos
@ Adam: no, no lo es. Todo estaba destinado a ser un conjunto de herramientas de desarrollador para Windows Desktop Gadgets, pero nunca lo terminé (aunque la consola funciona muy bien). Podría terminarlo en algún momento, a pesar de que los WDG no tienen un futuro muy brillante en este momento.
Andy E
3
Hace unas semanas, cambiamos la implementación de nuestra consola en Chrome de block a algún símbolo mágico porque con block bloqueó algunas características de ES6 :)
Alexey Kozyatinskiy
54

Si, si y si. Hay un uso muy legítimo. Reloj:

with (document.getElementById("blah").style) {
    background = "black";
    color = "blue";
    border = "1px solid green";
}

Básicamente, cualquier otro gancho DOM o CSS son usos fantásticos de with. No es que "CloneNode" esté indefinido y vuelva al ámbito global a menos que se salga de su camino y decida hacerlo posible.

La queja de velocidad de Crockford es que se crea un nuevo contexto con. Los contextos son generalmente caros. Estoy de acuerdo. Pero si acaba de crear un div y no tiene un marco disponible para configurar su CSS y necesita configurar 15 o más propiedades CSS a mano, entonces crear un contexto probablemente será más barato que la creación de variables y 15 desreferencias:

var element = document.createElement("div"),
    elementStyle = element.style;

elementStyle.fontWeight = "bold";
elementStyle.fontSize = "1.5em";
elementStyle.color = "#55d";
elementStyle.marginLeft = "2px";

etc ...


fuente
55
+1, ya que también creo que hay muchos usos legítimos de with. Sin embargo, en este caso particular, podría hacer:element.style.cssText="background: black ; color: blue ; border: 1px solid green"
GetFree
55
Se podría lograr lo mismo en una línea utilizando el sencillo extendmétodo de cualquiera de jQuery o Underscore.js: $.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'}).
Trevor Burnham
99
@TrevorBurnham - Si vas a asumir que jQuery está disponible, simplemente usarías su .css()método ...
nnnnnn
44
¿Qué es exactamente lo que impide que estas variables lleguen a alcance global? ¿Es solo porque todos los estilos CSS siempre se definen en todos los elementos, o qué?
mpen
1
@Mark sí, siempre están definidos, con valores nulos o cadenas vacías como valores si no hay un estilo personalizado para una propiedad
Esailija
34

Puede definir una pequeña función auxiliar para proporcionar los beneficios withsin la ambigüedad:

var with_ = function (obj, func) { func (obj); };

with_ (object_name_here, function (_)
{
    _.a = "foo";
    _.b = "bar";
});
John Millikin
fuente
8
¡Dios mío, mi cabeza explotó! sin la ambigüedad? ¡Tengo que votar eso, hombre!
Jarrod Dixon
@Jarrod: ¿qué tiene de divertido esto ( is.gd/ktoZ )? La mayoría de las personas que usan este sitio son más inteligentes que yo, así que perdónenme si me equivoco, pero esto parece una mala información.
cuervo
14
Pero eso es más largo y más difícil de entender: var _ = obj_name_here; _.a = "foo"; _.b = "bar;
Rene Saarsoo
3
Rene: Con eso, expondrás la variable "_" al alcance externo, lo que provocará posibles errores. También expondrá cualquier variable temporal utilizada en el cálculo de los parámetros del objeto.
John Millikin
30
Obtendrá más errores al with_ser una versión duplicada fangosa (function(_){ _.a="foo"; })(object_here);(la forma estándar de simular bloques de estilo c / java). Use eso en su lugar.
mk.
25

Apenas parece que valga la pena, ya que puede hacer lo siguiente:

var o = incrediblyLongObjectNameThatNoOneWouldUse;
o.name = "Bob";
o.age = "50";
Allain Lalonde
fuente
1
Esto no tenía ningún sentido para mí. Aunque no soy un experto en JavaScript
JeroenEijkhof
@WmasterJ Para mayor claridad, vea esta publicación: yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
Dennis
8
Los nombres largos de variables no son el único caso de uso para with.
Chris
18

Nunca uso, no veo una razón para hacerlo, y no lo recomiendo.

El problema withes que evita numerosas optimizaciones léxicas que puede realizar una implementación de ECMAScript. Dado el aumento de los motores rápidos basados ​​en JIT, este problema probablemente será aún más importante en el futuro cercano.

Puede parecer que withpermite construcciones más limpias (cuando, por ejemplo, introduce un nuevo alcance en lugar de una función anónima común o reemplaza el alias detallado), pero realmente no vale la pena . Además de una disminución del rendimiento, siempre existe el peligro de asignar a una propiedad de un objeto incorrecto (cuando no se encuentra la propiedad en un objeto en el alcance inyectado) y tal vez introducir erróneamente variables globales. IIRC, este último tema es el que motivó a Crockford a recomendar evitar with.

kangax
fuente
66
El hombre del saco de rendimiento se trota con frecuencia, casi tan a menudo como lo global ... Siempre me parece extraño, dado que estamos hablando de JavaScript . Asumirías que el éxito en el rendimiento es realmente dramático para garantizar tanta atención, pero ... Si tienes números concretos sobre el costo de las with(){}construcciones como las que se dan en otras respuestas aquí, en los navegadores modernos, me encantaría ver ¡ellos!
Shog9
66
¿Por qué es extraño en el contexto de Javascript? :) Y sí, es dramático. Piénselo: una implementación debe evaluar una expresión entre paréntesis, convertirla en objeto, insertarla en el frente de la cadena de alcance actual, evaluar la declaración dentro del bloque y luego restaurar la cadena de alcance a la normalidad. Eso es mucho trabajo. Mucho más que una simple búsqueda de propiedades que se puede convertir en un código de bajo nivel altamente optimizado. Aquí hay un punto de referencia muy simple que acabo de hacer (avíseme si encuentra algún error) que demuestre la diferencia - gist.github.com/c36ea485926806020024
kangax
55
@kangax: vengo de un entorno C ++, donde es tradicional que muchos programadores se obsesionen con las pequeñas eficiencias en su código, incluso cuando en realidad no tienen un efecto notable en el rendimiento de la rutina o programa más grande. Me parece extraño en el contexto de JavaScript, donde una gran parte del rendimiento de una rutina puede depender de la implementación de la VM. He visto algunas instancias en las que los programadores de JS evitarán, por ejemplo, una función anónima debido a preocupaciones sobre el costo de instalación, pero esta parece ser la excepción, no la regla, reservada para áreas muy sensibles del código.
Shog9
55
Dicho esto, tienes toda la razón con respecto al costo de with(){}: configurar un nuevo alcance withes enormemente costoso en cada navegador que probé. Desea evitar esto en cualquier código llamado con mucha frecuencia. Además, Chrome exhibió un éxito dramático para cualquier código que se ejecute dentro de un with()alcance. Curiosamente, IE tenía las mejores características de rendimiento para el código dentro de los with()bloques: factorizar el costo de configuración, with()proporciona el medio más rápido de acceso de miembros en las máquinas virtuales IE6 e IE8 (aunque estas máquinas virtuales son las más lentas en general). Buenas cosas, gracias ...
Shog9
55
FWIW: aquí está el mismo conjunto de pruebas con los costos de configuración factorizados: jsbin.com/imidu/edit ¡ El acceso variable with()es casi un orden de magnitud más lento en Chrome, y más del doble de rápido en IE ...!
Shog9
13

Visual Basic.NET tiene una Withdeclaración similar . Una de las formas más comunes en que lo uso es establecer rápidamente una serie de propiedades. En vez de:

someObject.Foo = ''
someObject.Bar = ''
someObject.Baz = ''

, Puedo escribir:

With someObject
    .Foo = ''
    .Bar = ''
    .Baz = ''
End With

Esto no es solo una cuestión de pereza. También hace que el código sea mucho más legible. Y a diferencia de JavaScript, no sufre de ambigüedad, ya que debe prefijar todo lo afectado por la declaración con un .(punto). Entonces, los dos siguientes son claramente distintos:

With someObject
    .Foo = ''
End With

vs.

With someObject
    Foo = ''
End With

El primero es someObject.Foo; este último está Fooen el alcance exterior someObject .

Creo que la falta de distinción de JavaScript lo hace mucho menos útil que la variante de Visual Basic, ya que el riesgo de ambigüedad es demasiado alto. Aparte de eso, withsigue siendo una idea poderosa que puede mejorar la legibilidad.

Sören Kuklau
fuente
2
Suficientemente cierto. Sin embargo, no responde a su pregunta. Entonces está fuera de tema.
Allain Lalonde
66
Esto también me pasaba por la mente. Alguien tuvo que decirlo . ¿Por qué JavaScript no puede tener solo el punto también?
Carson Myers,
De acuerdo, la notación de puntos es superior, deseo que JavaScript la use. +1
8

Puede usar withpara introducir el contenido de un objeto como variables locales en un bloque, como se está haciendo con este pequeño motor de plantillas .

Jordão
fuente
1
Esta técnica también se está utilizando en underscore.js's _.template () .
Peter V. Mørch
7

Usar "con" puede hacer que su código sea más seco.

Considere el siguiente código:

var photo = document.getElementById('photo');
photo.style.position = 'absolute';
photo.style.left = '10px';
photo.style.top = '10px';

Puedes secarlo de la siguiente manera:

with(document.getElementById('photo').style) {
  position = 'absolute';
  left = '10px';
  top = '10px';
}

Supongo que depende de si tienes preferencia por la legibilidad o la expresividad.

El primer ejemplo es más legible y probablemente se recomienda para la mayoría de los códigos. Pero la mayoría del código es bastante manso de todos modos. El segundo es un poco más oscuro, pero usa la naturaleza expresiva del lenguaje para reducir el tamaño del código y las variables superfluas.

Me imagino que las personas a las que les gusta Java o C # elegirían la primera forma (object.member) y las que prefieren Ruby o Python elegirían la última.

Jonás
fuente
Vaya, no me di cuenta de que alguien ya publicó básicamente este mismo ejemplo hace un año. Dejando a un lado los problemas de rendimiento, "con" es un buen código DRY a costa de ser un poco más difícil de leer. Creo que para colaboraciones con otros desarrolladores o la mayoría de los códigos de producción, es una buena práctica evitar la palabra clave "con". Pero si está trabajando con programadores de nivel experto y entiende cómo evitar posibles ineficiencias, vaya a la ciudad con "con".
Jonás
6

Creo que el uso obvio es como un atajo. Si, por ejemplo, está inicializando un objeto, simplemente guarde escribiendo mucho "ObjectName". Algo así como "con-slots" de lisp que te permite escribir

(with-slots (foo bar) objectname
   "some code that accesses foo and bar"

que es lo mismo que escribir

"some code that accesses (slot-value objectname 'foo) and (slot-value objectname 'bar)""

Es más obvio por qué este es un acceso directo que cuando su idioma permite "Objectname.foo" pero aún así.

Sarien
fuente
1
genial para ver el código lisp! Creo que "with" en javascript obviamente está inspirado en las raíces de su esquema como lenguaje, pero lamentablemente te están rechazando por publicar LISP en una pregunta de javascript.
Fire Crow
1
Votación a favor del principio básico. 'con' es una construcción fenomenalmente poderosa. Pero la mayoría de las personas de JS no entienden los cierres y escriben sistemas de herencia de clase Java ridículamente complejos además de JS, entonces, ¿cómo podrían ser conscientes del potencial de metaprogramación que 'con' ofrece?
Jared
Por supuesto, with-slotsrequiere que especifique qué ranuras está usando, mientras withque usará cualquier ranura que esté vinculada en tiempo de ejecución.
Samuel Edwin Ward
6

Al tener experiencia con Delphi, diría que usar con debería ser una optimización de tamaño de último recurso, posiblemente realizada por algún tipo de algoritmo de minimización de JavaScript con acceso al análisis de código estático para verificar su seguridad.

Los problemas de alcance que puede tener con el uso liberal de la declaración with pueden ser un verdadero problema en el a ** y no quisiera que nadie experimente una sesión de depuración para descubrir qué está pasando en su código. , solo para descubrir que capturó un miembro de objeto o la variable local incorrecta, en lugar de la variable de alcance global o externo que pretendía.

El VB con declaración es mejor, ya que necesita los puntos para desambiguar el alcance, pero el Delphi con declaración es un arma cargada con un disparador de hairt, y me parece que el javascript es lo suficientemente similar como para garantizar la misma advertencia.

Lasse V. Karlsen
fuente
55
El javascript con declaración es peor que el de Delphi. En Delphi, with funciona igual de rápido (si no más rápido) que la notación object.member. En javascript, tiene que recorrer el alcance para verificar si hay miembros coincidentes, por lo que siempre es más lento que la notación object.member.
Martijn
5

No se recomienda usar con, y está prohibido en el modo estricto ECMAScript 5. La alternativa recomendada es asignar el objeto cuyas propiedades desea acceder a una variable temporal.

Fuente: Mozilla.org

ianaz
fuente
4

La instrucción with puede usarse para disminuir el tamaño del código o para miembros de clases privadas, por ejemplo:

// demo class framework
var Class= function(name, o) {
   var c=function(){};
   if( o.hasOwnProperty("constructor") ) {
       c= o.constructor;
   }
   delete o["constructor"];
   delete o["prototype"];
   c.prototype= {};
   for( var k in o ) c.prototype[k]= o[k];
   c.scope= Class.scope;
   c.scope.Class= c;
   c.Name= name;
   return c;
}
Class.newScope= function() {
    Class.scope= {};
    Class.scope.Scope= Class.scope;
    return Class.scope;
}

// create a new class
with( Class.newScope() ) {
   window.Foo= Class("Foo",{
      test: function() {
          alert( Class.Name );
      }
   });
}
(new Foo()).test();

La instrucción with es muy útil si desea modificar el alcance, lo que es necesario para tener su propio alcance global que pueda manipular en tiempo de ejecución. Puede ponerle constantes o ciertas funciones auxiliares que se usan a menudo como, por ejemplo, "toUpper", "toLower" o "isNumber", "clipNumber", etc.

Sobre el mal rendimiento que leo a menudo: el alcance de una función no tendrá ningún impacto en el rendimiento, de hecho, en mi FF, una función de alcance se ejecuta más rápido que un sin alcance:

var o={x: 5},r, fnRAW= function(a,b){ return a*b; }, fnScoped, s, e, i;
with( o ) {
    fnScoped= function(a,b){ return a*b; };
}

s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
    r+= fnRAW(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );

s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
    r+= fnScoped(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );

Entonces, de la manera mencionada anteriormente, la declaración with no tiene un efecto negativo en el rendimiento, pero es bueno, ya que disminuye el tamaño del código, lo que afecta el uso de la memoria en los dispositivos móviles.

alex
fuente
3

Usar con también hace que su código sea más lento en muchas implementaciones, ya que ahora todo queda envuelto en un alcance adicional para la búsqueda. No hay una razón legítima para usar con JavaScript.

Svend
fuente
55
Optimización prematura. No reclames "más lento" a menos que hayas cruzado los números; cualquier sobrecarga es probablemente trivial tanto en impls js modernas como antiguas.
mk.
2
Estoy totalmente en desacuerdo con su conclusión, en base a lo que puede no ser un problema para los desarrolladores que no sean usted.
Dave Van den Eynde
44
@mk: ok, el cálculo de números para ti aquí: var obj={a:0,b:0,c:0};var d=+new Date;with(obj){for(var i=0;i<1000000;++i){a+=1;b+=1;c+=1}}+new Date-d;da un promedio de 2500, mientras que var obj={a:0,b:0,c:0};var d=+new Date;for(var i=0;i<1000000;++i){obj.a+=1;obj.b+=1;obj.c+=1}+new Date-d;da un promedio de 750, haciendo que el que usa sea más de 3 veces más lento.
yorick
3
Acabo de ejecutar estos en Chrome 23 en la consola cuando vi esto. Los resultados que obtuve fueron 1138 para el withcódigo y 903 sin él. Con esta pequeña diferencia, incluso en un ciclo cerrado, haría una selección basada en la simplicidad de codificación y la facilidad de refactorización caso por caso antes de preocuparme por el rendimiento.
Plynx
3

Creo que la declaración with puede ser útil al convertir un lenguaje de plantilla en JavaScript. Por ejemplo, JST en base2 , pero lo he visto con más frecuencia.

Estoy de acuerdo en que uno puede programar esto sin la declaración with. Pero debido a que no da ningún problema, es un uso legítimo.

Doekman
fuente
3

Es bueno para poner código que se ejecuta en un entorno relativamente complicado en un contenedor: lo uso para hacer un enlace local para "ventana" y para ejecutar código destinado a un navegador web.

aredridel
fuente
3

Creo que el uso literal del objeto es interesante, como un reemplazo directo para usar un cierre

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       (function(info)
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       })(data[i]);
}

o con la declaración equivalente de un cierre

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       with({info: data[i]})
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       }        
}

Creo que el riesgo real es accidentalmente minipular variables que no son parte de la declaración with, por lo que me gusta el objeto literal con el que se pasa, puede ver exactamente lo que será en el contexto agregado en el código.

Cuervo de fuego
fuente
3

Creé una función de "fusión" que elimina parte de esta ambigüedad con la withdeclaración:

if (typeof Object.merge !== 'function') {
    Object.merge = function (o1, o2) { // Function to merge all of the properties from one object into another
        for(var i in o2) { o1[i] = o2[i]; }
        return o1;
    };
}

Puedo usarlo de manera similar with, pero sé que no afectará ningún alcance que no pretendo que afecte.

Uso:

var eDiv = document.createElement("div");
var eHeader = Object.merge(eDiv.cloneNode(false), {className: "header", onclick: function(){ alert("Click!"); }});
function NewObj() {
    Object.merge(this, {size: 4096, initDate: new Date()});
}
palswim
fuente
3

Para algunas piezas de código cortas, me gustaría usar las funciones trigonométricas como sin, cosetc. en modo grado en lugar de en modo radiante. Para este propósito, uso un AngularDegreeobjeto:

AngularDegree = new function() {
this.CONV = Math.PI / 180;
this.sin = function(x) { return Math.sin( x * this.CONV ) };
this.cos = function(x) { return Math.cos( x * this.CONV ) };
this.tan = function(x) { return Math.tan( x * this.CONV ) };
this.asin = function(x) { return Math.asin( x ) / this.CONV };
this.acos = function(x) { return Math.acos( x ) / this.CONV };
this.atan = function(x) { return Math.atan( x ) / this.CONV };
this.atan2 = function(x,y) { return Math.atan2(x,y) / this.CONV };
};

Entonces puedo usar las funciones trigonométricas en modo de grado sin más ruido de lenguaje en un withbloque:

function getAzimut(pol,pos) {
  ...
  var d = pos.lon - pol.lon;
  with(AngularDegree) {
    var z = atan2( sin(d), cos(pol.lat)*tan(pos.lat) - sin(pol.lat)*cos(d) );
    return z;
    }
  }

Esto significa: uso un objeto como una colección de funciones, que habilito en una región de código limitado para acceso directo. Esto me parece útil.

rplantiko
fuente
no es una buena idea usar with declaración de esta manera, solo hace que el código sea difícil de leer porque no sabe qué función es global y qué función se llama en el ámbito de con objeto, por lo que si alguna función no está definida en el alcance del objeto, entonces intentará acceder a él en el espacio de nombres global
Saket Patel
Siendo consciente del problema de alcance, todavía lo encuentro útil. Un matemático que lee el código quiere ver directamente que la fórmula anterior es una aplicación de la "ley de las 4 partes sucesivas" en la trigonometría esférica. La alternativa estricta ofusca la fórmula: z = Math.atan2( Math.sin(d * Math.PI / 180), Math.cos( pol.lat * Math.PI / 180) * Math.tan( pos.lat * Math.PI / 180 ) - Math.sin( pol.lat * Math.PI / 180 ) * Math.cos( d * Math.PI / 180) ) * 180 / Math.PI;daría el mismo resultado, pero es el horror.
rplantiko
@rplantiko Lo que hay que tener en cuenta es que la mayoría de las personas no se sienten cómodas con él. Entonces, a menos que esté escribiendo un código que nadie más tocará. Además, estoy bastante seguro de que puedo mostrarte un par de usos withque te dejarían perplejo.
Juan Mendes
2

Creo que la utilidad de withpuede depender de qué tan bien esté escrito su código. Por ejemplo, si está escribiendo un código que aparece así:

var sHeader = object.data.header.toString();
var sContent = object.data.content.toString();
var sFooter = object.data.footer.toString();

entonces podría argumentar que withmejorará la legibilidad del código al hacer esto:

var sHeader = null, sContent = null, sFooter = null;
with(object.data) {
    sHeader = header.toString();
    sContent = content.toString();
    sFooter = content.toString();
}

Por el contrario, se podría argumentar que está violando la Ley de Deméter , pero, de nuevo, tal vez no. Me estoy desviando =).

Por encima de todo, sepa que Douglas Crockford recomienda no usar with. Le insto a que consulte su publicación de blog sobre withy sus alternativas aquí .

Tom
fuente
Gracias por la respuesta, Tom. He leído la recomendación de Crockford, y aunque tiene sentido, solo va tan lejos. Me estoy acercando a la idea, tocada indirectamente por doekman, de que el verdadero poder de con {} está en la forma en que puede usarse para manipular el alcance ...
Shog9
2

Simplemente no veo cómo usar el with es más legible que simplemente escribir object.member. No creo que sea menos legible, pero tampoco creo que sea más legible.

Como dijo lassevk, definitivamente puedo ver cómo usar con sería más propenso a errores que simplemente usar la sintaxis muy explícita "object.member".

17 de 26
fuente
1

Debe ver la validación de un formulario en javascript en W3schools http://www.w3schools.com/js/js_form_validation.asp donde el formulario del objeto se "escanea" para encontrar una entrada con el nombre 'correo electrónico'

Pero lo modifiqué para obtener de CUALQUIER formulario todos los campos se validan como no vacíos, independientemente del nombre o la cantidad de campo en un formulario. Bueno, he probado solo campos de texto.

Pero con () hizo las cosas más simples. Aquí está el código:

function validate_required(field)
{
with (field)
  {
  if (value==null||value=="")
    {
    alert('All fields are mandtory');return false;
    }
  else
    {
    return true;
    }
  }
}

function validate_form(thisform)
{
with (thisform)
  {
    for(fiie in elements){
        if (validate_required(elements[fiie])==false){
            elements[fiie].focus();
            elements[fiie].style.border='1px solid red';
            return false;
        } else {elements[fiie].style.border='1px solid #7F9DB9';}
    }

  }
  return false;
}
Elvis Salaris
fuente
1

La bifurcación Coco de CoffeeScript tiene una withpalabra clave, pero simplemente se establece this(también se puede escribir como @en CoffeeScript / Coco) al objeto de destino dentro del bloque. Esto elimina la ambigüedad y logra el cumplimiento del modo estricto ES5:

with long.object.reference
  @a = 'foo'
  bar = @b
Trevor Burnham
fuente
0

Aquí hay un buen uso para with : agregar nuevos elementos a un objeto literal, en función de los valores almacenados en ese objeto. Aquí hay un ejemplo que acabo de usar hoy:

Tenía un conjunto de fichas posibles (con aberturas hacia arriba, abajo, izquierda o derecha) que se podían usar, y quería una forma rápida de agregar una lista de fichas que siempre se colocarían y bloquearían al comienzo del juego. . No quería seguir escribiendo types.tbrpara cada tipo en la lista, así que simplemente lo usé with.

Tile.types = (function(t,l,b,r) {
  function j(a) { return a.join(' '); }
  // all possible types
  var types = { 
    br:  j(  [b,r]),
    lbr: j([l,b,r]),
    lb:  j([l,b]  ),  
    tbr: j([t,b,r]),
    tbl: j([t,b,l]),
    tlr: j([t,l,r]),
    tr:  j([t,r]  ),  
    tl:  j([t,l]  ),  
    locked: []
  };  
  // store starting (base/locked) tiles in types.locked
  with( types ) { locked = [ 
    br,  lbr, lbr, lb, 
    tbr, tbr, lbr, tbl,
    tbr, tlr, tbl, tbl,
    tr,  tlr, tlr, tl
  ] } 
  return types;
})("top","left","bottom","right");
avanderveen
fuente
0

Puede usar with para evitar tener que administrar explícitamente arity cuando use require.js:

var modules = requirejs.declare([{
    'App' : 'app/app'
}]);

require(modules.paths(), function() { with (modules.resolve(arguments)) {
    App.run();
}});

Implementación de requirejs.declare:

requirejs.declare = function(dependencyPairs) {
    var pair;
    var dependencyKeys = [];
    var dependencyValues = [];

    for (var i=0, n=dependencyPairs.length; i<n; i++) {
        pair = dependencyPairs[i];
        for (var key in dependencyPairs[i]) {
            dependencyKeys.push(key);
            dependencyValues.push(pair[key]);
            break;
        }
    };

    return {
        paths : function() {
            return dependencyValues;
        },

        resolve : function(args) {
            var modules = {};
            for (var i=0, n=args.length; i<n; i++) {
                modules[dependencyKeys[i]] = args[i];
            }
            return modules;
        }
    }   
}
Dexygen
fuente
0

Como Andy E señaló en los comentarios de la respuesta de Shog9, este comportamiento potencialmente inesperado ocurre cuando se usa withcon un objeto literal:

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with ({num: i}) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "[object Object]"
  }
}

No es que el comportamiento inesperado ya no fuera un sello distintivo with.

Si realmente desea utilizar esta técnica, al menos use un objeto con un prototipo nulo.

function scope(o) {
  var ret = Object.create(null);
  if (typeof o !== 'object') return ret;
  Object.keys(o).forEach(function (key) {
    ret[key] = o[key];
  });
  return ret;
}

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with (scope({num: i})) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "a"
  }
}

Pero esto solo funcionará en ES5 +. Tampoco lo uses with.

Jackson
fuente
0

Estoy trabajando en un proyecto que permitirá a los usuarios cargar código para modificar el comportamiento de partes de la aplicación. En este escenario, he estado usando una withcláusula para evitar que su código modifique cualquier cosa fuera del alcance con el que quiero que jueguen. La parte (simplificada) del código que uso para hacer esto es:

// this code is only executed once
var localScope = {
    build: undefined,

    // this is where all of the values I want to hide go; the list is rather long
    window: undefined,
    console: undefined,
    ...
};
with(localScope) {
    build = function(userCode) {
        eval('var builtFunction = function(options) {' + userCode + '}');
        return builtFunction;
    }
}
var build = localScope.build;
delete localScope.build;

// this is how I use the build method
var userCode = 'return "Hello, World!";';
var userFunction = build(userCode);

Este código asegura (de alguna manera) que el código definido por el usuario no tiene acceso a ningún objeto con alcance global, como windowtampoco a ninguna de mis variables locales a través de un cierre.

Solo como una palabra para el sabio, todavía tengo que realizar comprobaciones de código estático en el código enviado por el usuario para asegurarme de que no estén usando otras maneras furtivas para acceder al alcance global. Por ejemplo, el siguiente código definido por el usuario toma acceso directo a window:

test = function() {
     return this.window
};
return test();
kevin.groat
fuente
0

Mi

switch(e.type) {
    case gapi.drive.realtime.ErrorType.TOKEN_REFRESH_REQUIRED: blah
    case gapi.drive.realtime.ErrorType.CLIENT_ERROR: blah
    case gapi.drive.realtime.ErrorType.NOT_FOUND: blah
}

se reduce a

with(gapi.drive.realtime.ErrorType) {switch(e.type) {
    case TOKEN_REFRESH_REQUIRED: blah
    case CLIENT_ERROR: blah
    case NOT_FOUND: blah
}}

¿Puedes confiar en un código de tan baja calidad? No, vemos que se hizo absolutamente ilegible. Sin lugar a dudas, este ejemplo demuestra que no hay necesidad de declaración con, si estoy tomando la legibilidad correctamente;)

Pequeño alien
fuente