Existe-t-il des utilisations légitimes de la déclaration «avec» de JavaScript?


369

Les commentaires d'Alan Storm en réponse à ma réponse concernant la withdéclaration m'ont fait réfléchir. J'ai rarement trouvé une raison d'utiliser cette fonctionnalité de langue particulière, et je n'avais jamais beaucoup réfléchi à la façon dont cela pourrait causer des problèmes. Maintenant, je suis curieux de savoir comment je pourrais utiliser efficacement withtout en évitant ses pièges.

Où avez-vous trouvé l' withénoncé utile?


52
Je ne l'utilise jamais. C'est plus facile de vivre sans si je fais comme si ça n'existait pas.
Nosredna

6
Il peut y avoir eu de nombreuses utilisations valides. Mais c'est sans objet. ES5 Strict supprimé with, il n'y a plus rien de tel.
Thomas Aylott

27
Il convient de noter ici que ES5 Strict est toujours facultatif .
Shog9

5
Au lieu de supprimer «avec» dans ES5 strict, n'aurait-il pas été préférable de modifier la norme de sorte que si aucune variable n'est trouvée, toute affectation effectuée à l'intérieur de «avec» soit liée à l'objet argument?
juillet 2011

2
@JussiR: Probablement. Mais le problème en faisant cela, c'est que cela pourrait casser les choses dans les navigateurs plus anciens.
Sune Rasmussen

Réponses:


520

Une autre utilisation m'est venue aujourd'hui, alors j'ai fouillé le Web avec enthousiasme et j'en ai trouvé une mention existante: Définir des variables dans l'étendue du bloc .

Contexte

JavaScript, malgré sa ressemblance superficielle avec C et C ++, n'étend pas les variables au bloc dans lequel elles sont définies:

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

Déclarer une fermeture dans une boucle est une tâche courante lorsque cela peut entraîner des erreurs:

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

Étant donné que la boucle for n'introduit pas de nouvelle étendue, la même num - avec une valeur de 2- sera partagée par les trois fonctions.

Une nouvelle portée: letetwith

Avec l'introduction de l' letinstruction dans ES6 , il devient facile d'introduire une nouvelle portée lorsque cela est nécessaire pour éviter ces problèmes:

// 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);
}

Ou même:

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);
}

Jusqu'à ce qu'ES6 soit universellement disponible, cette utilisation reste limitée aux navigateurs et développeurs les plus récents désireux d'utiliser des transpilers. Cependant, nous pouvons facilement simuler ce comportement en utilisant 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);
   }
}

La boucle fonctionne maintenant comme prévu, créant trois variables distinctes avec des valeurs de 0 à 2. Notez que les variables déclarées dans le bloc ne sont pas étendues à celui-ci, contrairement au comportement des blocs en C ++ (en C, les variables doivent être déclarées au début de un bloc, donc en quelque sorte il est similaire). Ce comportement est en fait assez similaire à une letsyntaxe de bloc introduite dans les versions antérieures des navigateurs Mozilla, mais n'est pas largement adopté ailleurs.


15
Jamais pensé à utiliser avec un littéral, semble légitime.
Matt Kantor

81
C'est vraiment vraiment mort. Je n'ai jamais pensé à jouer avec la portée de JavaScript de cette façon. De nouveaux domaines totalement élargis à mon codage. J'aimerais pouvoir voter 10 fois!
kizzx2

27
Pour ceux qui s'y opposent encore, on pourrait toujours utiliser une fermeture:for (var i = 0; i < 3; ++i) { setTimeout ((function () { var num = i; return function () { alert (num); }; }) (), 10);}
Thomas Eding

4
En fait, le problème lié ci-dessus apparaît sur la plupart des navigateurs non Mozilla (Chrome, Safari, Opera, IE).
Max Shawabkeh

24
laissez le support de déclaration dans IE sauverait vraiment mon bacon en ce moment, je me bats avec ma conscience pour savoir si je dois l'utiliser à la place. Le vrai problème est que même avec un avec comme let , un soin supplémentaire doit encore être pris en raison des propriétés héritées d'un objet sur la chaîne de prototype. Par exemple var toString = function () { return "Hello"; }; with ({"test":1}) { console.log(toString()); };,. Dans la portée de l' instruction with , toString () est une propriété héritée de Object , donc la fonction explicitement définie n'est pas appelée. Encore une bonne réponse, cependant :-)
Andy E

161

J'ai utilisé l'instruction with comme une forme simple d'importation à portée limitée. Disons que vous avez un générateur de balisage quelconque. Plutôt que d'écrire:

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

Vous pouvez plutôt écrire:

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

Pour ce cas d'utilisation, je ne fais aucune affectation, donc je n'ai pas le problème d'ambiguïté associé à cela.


5
Voilà comment je l'ai vu utilisé en VB. (Et la seule utilisation que je connaissais.)
Mateen Ulhaq

2
Un inconvénient serait que si vous référencez une variable dans le bloc with qui se trouve en dehors de l'objet markupbuilder, le moteur js la cherchera d'abord dans markupbuilder de toute façon, réduisant les performances.
Adam Thomas

3
Cela aide vraiment à réduire le code pour ceux qui travaillent avec des chemins de canevas.
Brian McCutchon

4
La version "avec" de ce code fonctionne littéralement plus de 240 fois plus lentement sur ma machine que la version "non avec" de la même chose. C'est pourquoi les gens disent qu'il n'y a pas d'utilisation légitime. Pas parce qu'il ne peut pas rendre le code plus joli à certains endroits. Voir benchmark: jsfiddle.net/sc46eeyn
Jimbo Jonny

1
@McBrainy - C'est exactement le type d'endroit que vous ne devriez pas utiliser de code qui s'exécute plus lentement (voir le commentaire que je viens de faire au-dessus de celui-ci). Si vous avez besoin de raccourcis pour du code super répété, vous pouvez les déclarer. Par exemple, si vous utilisez context.bezierCurveToune centaine de fois, vous pouvez dire var bc2 = context.bezierCurveTo;et ensuite aller à bc2(x,x,etc);chaque fois que vous voulez l'appeler. C'est assez rapide et encore moins verbeux, alors que withc'est super lent.
Jimbo Jonny

83

Comme mes commentaires précédents l'ont indiqué, je ne pense pas que vous puissiez l'utiliser en withtoute sécurité, aussi tentant que cela puisse être dans une situation donnée. Étant donné que le problème n'est pas directement abordé ici, je le répète. Considérez le code suivant

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

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

Sans étudier attentivement ces appels de fonction, il n'y a aucun moyen de savoir quel sera l'état de votre programme après l'exécution de ce code. Si elle user.nameétait déjà définie, elle le sera désormais Bob. S'il n'a pas été défini, le global namesera initialisé ou changé en Bobet l' userobjet restera sans namepropriété.

Des bugs se produisent. Si vous utilisez avec vous finirez par le faire et augmenterez les chances d'échec de votre programme. Pire, vous pouvez rencontrer du code de travail qui définit un global dans le bloc with, délibérément ou par le biais de l'auteur ne connaissant pas cette bizarrerie de la construction. C'est un peu comme rencontrer un échec sur un commutateur, vous ne savez pas si l'auteur a voulu cela et il n'y a aucun moyen de savoir si la "correction" du code introduira une régression.

Les langages de programmation modernes regorgent de fonctionnalités. Certaines fonctionnalités, après des années d'utilisation, s'avèrent être mauvaises et doivent être évitées. Javascript withest l'un d'entre eux.


18
Ce problème n'apparaît que lorsque vous affectez des valeurs à l'attribut de l'objet. Mais que faire si vous ne l'utilisez que pour lire des valeurs? Je soutiens qu'il est acceptable de l'utiliser dans ce cas.
airportyh

10
Le même problème s'applique à la lecture des valeurs Toby. Dans l'extrait de code ci-dessus, vous ne savez pas si le nom est défini sur l'objet utilisateur, vous ne savez donc pas si vous lisez le nom global ou le nom d'utilisateur.
Alan Storm

12
Avec la lecture des valeurs, il existe une règle de priorité claire: les attributs sur l'objet sont vérifiés avant les variables en dehors de la portée. Ce n'est pas différent des variables de portée des fonctions. Le vrai problème avec l'affectation et «avec», si je comprends bien, réside dans le fait que l'attribution d'attribut se produit ou non dépend de l'existence de l'attribut sur l'objet actuel en question, qui est une propriété d'exécution et ne peut pas être déduit facilement en regardant le code.
airportyh

1
Je pense que tu es peut-être là Toby. Le problème d'écriture me suffit pour éviter complètement la construction.
Alan Storm

"C'est un peu comme rencontrer un échec sur un interrupteur, vous n'en avez aucune idée ..." - Alors, bannissons également switch ()? ;-p
Sz.

66

J'ai trouvé la withdéclaration incroyablement utile récemment. Cette technique ne m'est jamais venue à l'esprit tant que je n'ai pas commencé mon projet actuel - une console de ligne de commande écrite en JavaScript. J'essayais d'émuler les API de la console Firebug / WebKit où des commandes spéciales peuvent être entrées dans la console mais elles ne remplacent aucune variable dans la portée globale. J'y ai pensé en essayant de surmonter un problème que j'ai mentionné dans les commentaires de l'excellente réponse de Shog9 .

Pour obtenir cet effet, j'en ai utilisé deux avec des instructions pour "superposer" une étendue derrière l'étendue globale:

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

La grande chose à propos de cette technique est que, à part les inconvénients de performances, elle ne souffre pas des craintes habituelles de la withdéclaration, car nous évaluons de toute façon dans la portée globale - il n'y a aucun danger que des variables en dehors de notre pseudo-portée soient modifié.

J'ai été inspiré pour poster cette réponse lorsque, à ma grande surprise, j'ai réussi à trouver la même technique utilisée ailleurs - le code source 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);
}

EDIT: Je viens de vérifier la source de Firebug, ils enchaînent 4 avec des instructions pour encore plus de couches. Fou!

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);" +
    "}" +
"}}}}";

1
mais craignant qu'ecmascript5 ne vous empêche de le faire. Existe-t-il une solution ecmascript 5?
kybernetikos

@Adam: Je n'en suis pas sûr. ES5 ne génère une erreur pour cela qu'en mode strict, donc ce n'est pas un problème immédiat si vous n'avez pas de mode strict déclaré globalement. ES Harmony pourrait poser un problème plus important, mais il pourrait être corrigé avec certaines des nouveautés comme les proxys.
Andy E

@AndyE désolé que ce soit hors sujet, mais votre «console de ligne de commande écrite en JavaScript» est-elle disponible n'importe où?
kybernetikos

@Adam: non, ce n'est pas le cas. Le tout était destiné à être un ensemble d'outils de développement pour les gadgets de bureau Windows, mais je ne l'ai jamais terminé (bien que la console fonctionne très bien). Je pourrais le terminer à un moment donné, même si les WDG n'ont pas un avenir très brillant pour le moment.
Andy E

3
Il y a quelques semaines, nous avons déplacé notre implémentation de console dans Chrome d'un bloc à une magie de symbole, car avec un bloc bloqué certaines fonctionnalités ES6 :)
Alexey Kozyatinskiy

54

Oui, oui et oui. Il y a une utilisation très légitime. Regarder:

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

Fondamentalement, tous les autres crochets DOM ou CSS sont des utilisations fantastiques de with. Ce n'est pas comme si "CloneNode" serait indéfini et reviendrait à la portée globale à moins que vous ne vous y mettiez de côté et décidiez de le rendre possible.

La plainte de Crockford pour la vitesse est qu'un nouveau contexte est créé par avec. Les contextes sont généralement chers. Je suis d'accord. Mais si vous venez de créer un div et que vous n'avez pas de cadre disponible pour configurer votre CSS et que vous devez configurer 15 propriétés CSS à la main, la création d'un contexte sera probablement moins cher que la création de variables et 15 déréférences:

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

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

etc...


5
+1, car je pense également qu'il existe de nombreuses utilisations légitimes de with. Cependant, dans ce cas particulier, vous pouvez simplement faire:element.style.cssText="background: black ; color: blue ; border: 1px solid green"
GetFree

5
Vous pouvez obtenir la même chose en une ligne en utilisant simple extendméthode à partir soit jQuery ou Underscore.js: $.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'}).
Trevor Burnham

9
@TrevorBurnham - Si vous supposez que jQuery est disponible, vous devez simplement utiliser sa .css()méthode ...
nnnnnn

4
Qu'est-ce qui empêche exactement ces variables d'atteindre la portée mondiale? Est-ce simplement parce que tous les styles CSS sont toujours définis sur tous les éléments, ou quoi?
mpen

1
@Mark oui, ils sont toujours définis, avec des valeurs nulles ou vides comme valeurs s'il n'y a pas de style personnalisé pour une propriété
Esailija

34

Vous pouvez définir une petite fonction d'assistance pour fournir les avantages withsans ambiguïté:

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

with_ (object_name_here, function (_)
{
    _.a = "foo";
    _.b = "bar";
});

8
OMG, MA TÊTE ÉCLATÉE! sans l'ambiguïté? Je dois voter ça, mec!
Jarrod Dixon

@Jarrod: qu'est-ce qui est si drôle à ce sujet ( is.gd/ktoZ )? La plupart des personnes qui utilisent ce site sont plus intelligentes que moi, alors pardonnez-moi si je me trompe, mais cela semble être une mauvaise information.
corbeau

14
Mais c'est juste plus long et plus difficile à comprendre la façon de faire: var _ = obj_name_here; _.a = "foo"; _.b = "bar;
Rene Saarsoo

3
René: Avec cela, vous exposerez la variable "_" à la portée externe, ce qui entraînera des bogues potentiels. Il exposera également toutes les variables temporaires utilisées dans le calcul des paramètres d'objet.
John Millikin

30
Vous obtiendrez plus de bogues en with_étant une version boueuse doublée de (function(_){ _.a="foo"; })(object_here);(la façon standard de simuler des blocs de style c / java). Utilisez-le à la place.
mk.


18

Je n'utilise jamais avec, je ne vois pas de raison de le faire et je ne le recommande pas.

Le problème withest qu'il empêche de nombreuses optimisations lexicales qu'une implémentation ECMAScript peut effectuer. Étant donné l'essor des moteurs rapides basés sur JIT, ce problème deviendra probablement encore plus important dans un avenir proche.

Cela pourrait ressembler à withdes constructions plus propres (lorsque, par exemple, l'introduction d'une nouvelle portée au lieu d'un wrapper de fonction anonyme commun ou le remplacement d'un aliasing détaillé), mais cela n'en vaut vraiment pas la peine . Outre une baisse des performances, il y a toujours un danger d'attribuer à une propriété d'un mauvais objet (lorsque la propriété n'est pas trouvée sur un objet dans la portée injectée) et peut-être d'introduire à tort des variables globales. IIRC, ce dernier problème est celui qui a motivé Crockford à recommander d'éviter with.


6
Le croque-mitaine de la performance est souvent repéré, presque aussi souvent que la chose globale ... me semble toujours étrange, étant donné que c'est JavaScript dont nous parlons. Vous supposeriez que la performance est vraiment dramatique pour mériter autant d'attention, mais ... Si vous avez des chiffres précis sur le coût des with(){}constructions comme celles données dans d'autres réponses ici, dans les navigateurs modernes, j'aimerais voir leur!
Shog9

6
Pourquoi est-ce étrange dans le contexte de Javascript? :) Et oui, c'est dramatique. Pensez-y - une implémentation doit évaluer une expression entre parenthèses, la convertir en objet, l'insérer à l'avant de la chaîne de portée actuelle, évaluer l'instruction à l'intérieur du bloc, puis restaurer la chaîne de portée à la normale. Ça fait beaucoup de travail. Bien plus qu'une simple recherche de propriété qui peut être transformée en un code de bas niveau hautement optimisé. Voici un point de repère très simple que je viens de faire (faites-moi savoir si vous trouvez des erreurs) démontrant la différence - gist.github.com/c36ea485926806020024
kangax

5
@kangax: Je viens d'un arrière-plan C ++, où il est assez traditionnel pour de nombreux programmeurs de s'interroger sur les petites efficacités de leur code, même s'ils n'ont en fait pas d'effet notable sur les performances de la routine ou du programme plus large. Cela me semble étrange dans le contexte de JavaScript, où une si grande partie des performances d'une routine peut dépendre de l'implémentation de la machine virtuelle. J'ai vu quelques cas où les programmeurs JS éviteront, par exemple, une fonction anonyme en raison de préoccupations concernant le coût d'installation, mais cela semble être l'exception et non la règle, réservée aux zones de code très sensibles.
Shog9

5
Cela dit, vous avez absolument raison en ce qui concerne le coût de with(){}: la configuration d'une nouvelle portée avec withest extrêmement coûteuse sur tous les navigateurs que j'ai testés. Vous voudriez éviter cela dans tout code appelé très fréquemment. En outre, Chrome a affiché un succès spectaculaire pour tout code s'exécutant dans une with()étendue. Fait intéressant, IE avait les meilleures caractéristiques de performances pour le code dans les with()blocs: en tenant compte du coût d'installation, with()fournit le moyen le plus rapide d'accès des membres dans les machines virtuelles IE6 et IE8 (bien que ces machines virtuelles soient les plus lentes dans l'ensemble).
Du

5
FWIW: voici le même ensemble de tests avec les coûts d'installation pris en compte: jsbin.com/imidu/edit L'accès variable pour with()est presque un ordre de grandeur plus lent dans Chrome, et plus de deux fois plus rapide dans IE ...!
Shog9

13

Visual Basic.NET a une Withinstruction similaire . L'une des façons les plus courantes de l'utiliser est de définir rapidement un certain nombre de propriétés. Au lieu de:

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

, Je peux écrire:

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

Ce n'est pas seulement une question de paresse. Cela rend également le code beaucoup plus lisible. Et contrairement à JavaScript, il ne souffre pas d'ambiguïté, car vous devez préfixer tout ce qui est affecté par la déclaration par un .(point). Ainsi, les deux suivants sont clairement distincts:

With someObject
    .Foo = ''
End With

contre.

With someObject
    Foo = ''
End With

Le premier est someObject.Foo; ce dernier est Foodans le champ d'application extérieur someObject .

Je trouve que le manque de distinction de JavaScript le rend beaucoup moins utile que la variante de Visual Basic, car le risque d'ambiguïté est trop élevé. En dehors de cela, withc'est toujours une idée puissante qui peut améliorer la lisibilité.


2
Assez vrai. Mais ne répond pas à sa question. C'est donc hors sujet.
Allain Lalonde

6
Cela me traversait aussi l'esprit. quelqu'un devait le dire . Pourquoi JavaScript ne peut-il pas simplement avoir le point aussi.
Carson Myers

D'accord, la notation par points est supérieure, j'aimerais que JavaScript l'utilise. +1


7

L'utilisation de "avec" peut rendre votre code plus sec.

Considérez le code suivant:

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

Vous pouvez le sécher comme suit:

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

Je suppose que cela dépend si vous avez une préférence pour la lisibilité ou l'expressivité.

Le premier exemple est plus lisible et probablement recommandé pour la plupart des codes. Mais la plupart du code est assez apprivoisé de toute façon. Le second est un peu plus obscur mais utilise la nature expressive du langage pour réduire la taille du code et les variables superflues.

J'imagine que les gens qui aiment Java ou C # choisiraient la première façon (object.member) et ceux qui préfèrent Ruby ou Python choisiraient cette dernière.


Oups, je ne savais pas que quelqu'un avait déjà publié le même exemple il y a un an. Mis à part les problèmes de performances, "avec" rend le code DRY agréable au détriment d'être un peu plus difficile à lire. Je pense que pour les collaborations avec d'autres développeurs ou la plupart des codes de production, il est recommandé d'éviter le mot clé "avec". Mais si vous travaillez avec des programmeurs de niveau expert et comprenez comment éviter les inefficacités potentielles, alors allez en ville avec "with".
Jonah

6

Je pense que l'utilisation évidente est comme raccourci. Par exemple, si vous initialisez un objet, vous enregistrez simplement en tapant beaucoup de "ObjectName". Un peu comme les "with-slots" de lisp qui vous permettent d'écrire

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

ce qui revient à écrire

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

Il est plus évident pourquoi c'est un raccourci que lorsque votre langage autorise "Objectname.foo" mais quand même.


1
super de voir du code lisp! Je pense que "avec" en javascript est évidemment inspiré par ses racines de schéma en tant que langue, mais hélas, vous êtes rétrogradé pour publier LISP sur une question javascript.
Fire Crow du

1
Vote sur le principe de base. «avec» est une construction phénoménalement puissante. Mais la plupart des gens de JS ne comprennent pas les fermetures et écrivent des systèmes d'héritage de classes Java ridiculement complexes au-dessus de JS - alors comment pourraient-ils être conscients du potentiel de métaprogrammation qu'offre «avec»?
jared

Bien sûr, with-slotsvous devez spécifier les emplacements que vous utilisez, tandis que vous withutiliserez tous les emplacements liés à l'exécution.
Samuel Edwin Ward

6

Ayant de l'expérience avec Delphi, je dirais que l'utilisation avec devrait être une optimisation de taille de dernier recours, éventuellement réalisée par une sorte d'algorithme de minimiseur javascript avec accès à une analyse de code statique pour vérifier sa sécurité.

Les problèmes de portée que vous pouvez rencontrer avec une utilisation libérale de l' instruction with peuvent être une douleur royale dans l'a ** et je ne voudrais pas que quiconque expérimente une session de débogage pour comprendre ce qu'il .. se passe dans votre code , seulement pour découvrir qu'il a capturé un membre d'objet ou la mauvaise variable locale, au lieu de votre variable de portée globale ou externe que vous vouliez.

La déclaration VB with est meilleure, en ce sens qu'elle a besoin des points pour lever l'ambiguïté, mais la déclaration Delphi with est un pistolet chargé avec un déclencheur de cheveux, et il me semble que le javascript est assez similaire pour justifier le même avertissement.


5
L'instruction javascript with est pire que celle de Delphi. En Delphi, avec effectue tout aussi rapide (sinon plus rapide) que la notation object.member. En javascript, avec doit parcourir l'étendue pour rechercher les membres correspondants, ce qui le rend toujours plus lent que la notation object.member.
Martijn

5

L'utilisation de avec n'est pas recommandée et est interdite en mode strict ECMAScript 5. L'alternative recommandée consiste à affecter l'objet dont vous souhaitez accéder aux propriétés à une variable temporaire.

Source: Mozilla.org


4

L'instruction with peut être utilisée pour réduire la taille du code ou pour les membres d'une classe privée, par exemple:

// 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();

L'instruction with est très utile si vous souhaitez modifier la portée, ce qui est nécessaire pour avoir votre propre portée globale que vous pouvez manipuler au moment de l'exécution. Vous pouvez y mettre des constantes ou certaines fonctions d'assistance souvent utilisées comme par exemple "toUpper", "toLower" ou "isNumber", "clipNumber" aso ..

À propos des mauvaises performances que je lis souvent: la portée d'une fonction n'aura aucun impact sur les performances, en fait dans mon FF, une fonction de portée s'exécute plus rapidement qu'une fonction non étendue:

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" );

Ainsi, de la manière mentionnée ci-dessus, l'instruction with n'a aucun effet négatif sur les performances, mais elle est bonne car elle diminue la taille du code, ce qui affecte l'utilisation de la mémoire sur les appareils mobiles.


3

L'utilisation de avec rend également votre code plus lent dans de nombreuses implémentations, car tout est désormais enveloppé dans une portée supplémentaire pour la recherche. Il n'y a aucune raison légitime d'utiliser avec en JavaScript.


5
Optimisation prématurée. Ne prétendez pas «plus lentement» à moins d'avoir croisé les chiffres; tout frais généraux est probablement trivial sur les implants js modernes et anciens.
mk.

2
Je suis fortement en désaccord avec votre conclusion, basée sur ce qui peut ne pas être un problème pour les développeurs autres que vous.
Dave Van den Eynde

4
@mk: ok, le calcul des nombres pour vous ici: 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;donne en moyenne 2500, tandis 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;donne en moyenne 750, ce qui rend celui qui est utilisé plus de 3 fois plus lentement.
yorick

3
Je viens de les exécuter dans Chrome 23 dans la console lorsque j'ai vu cela. Les résultats que j'ai obtenus étaient 1138 pour le withcode et 903 sans. Avec cette petite différence, même dans une boucle serrée, je ferais une sélection basée sur la simplicité du codage et la facilité de refactorisation au cas par cas avant de se soucier des performances.
Plynx

3

Je pense que la déclaration with peut être utile lors de la conversion d'un langage de modèle en JavaScript. Par exemple JST en base2 , mais je l'ai vu plus souvent.

Je suis d'accord que l'on peut programmer cela sans la déclaration with. Mais parce qu'il ne pose aucun problème, c'est une utilisation légitime.


3

C'est bon pour mettre du code qui s'exécute dans un environnement relativement compliqué dans un conteneur: je l'utilise pour créer une liaison locale pour "window" et pour exécuter du code destiné à un navigateur Web.


3

Je pense que l'utilisation littérale de l'objet est intéressante, comme un remplacement direct pour l'utilisation d'une fermeture

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]);
}

ou la déclaration with équivalente à une clôture

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) };
       }        
}

Je pense que le vrai risque est de minipuler accidentellement des variables qui ne font pas partie de l'instruction with, c'est pourquoi j'aime que le littéral d'objet soit passé avec, vous pouvez voir exactement ce que ce sera dans le contexte ajouté dans le code.


3

J'ai créé une fonction de «fusion» qui élimine une partie de cette ambiguïté avec la withdéclaration:

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;
    };
}

Je peux l'utiliser de la même manière with, mais je peux savoir que cela n'affectera aucune portée que je n'ai pas l'intention d'affecter.

Usage:

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()});
}

3

Pour certains éléments de code courts, je voudrais utiliser les fonctions trigonométriques comme sin, cosetc. en mode degré plutôt qu'en mode radiant. Pour cela, j'utilise un AngularDegreeobjet:

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 };
};

Ensuite, je peux utiliser les fonctions trigonométriques en mode degré sans autre bruit de langage dans un withbloc:

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;
    }
  }

Cela signifie: j'utilise un objet comme un ensemble de fonctions, que j'active dans une région de code limitée pour un accès direct. Je trouve cela utile.


ce n'est pas une bonne idée d'utiliser l' withinstruction de cette façon, cela rend le code difficile à lire parce que vous ne savez pas quelle fonction est globale et quelle fonction est appelée dans la portée de with object, donc si une fonction n'est pas définie dans le portée de l'objet puis il essaiera d'y accéder dans l'espace de noms global
Saket Patel

Étant conscient du problème de cadrage, je trouve toujours cela utile. Un mathématicien qui lit le code veut voir directement que la formule ci-dessus est une application de la "loi des 4 parties successives" en trigonométrie sphérique. L'alternative stricte obscurcit la formule: 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;donnerait le même résultat, mais c'est l'horreur.
rplantiko

@rplantiko La chose à garder à l'esprit est que la plupart des gens ne s'y sentent pas à l'aise. Donc, sauf si vous écrivez du code que personne d'autre ne touchera jamais. De plus, je suis presque sûr de pouvoir vous montrer quelques utilisations withqui vous rendraient perplexe.
Juan Mendes

2

Je pense que l'utilité de withpeut dépendre de la qualité de l' écriture de votre code. Par exemple, si vous écrivez du code qui ressemble à ceci:

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

alors vous pourriez faire valoir que withcela améliorera la lisibilité du code en procédant comme suit:

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

À l'inverse, on pourrait faire valoir que vous violez la loi de Déméter , mais, encore une fois, peut-être pas. Je m'égare =).

Par-dessus tout, sachez que Douglas Crockford recommande de ne pas utiliser with. Je vous invite à consulter son article de blog concernant withet ses alternatives ici .


Merci pour la réponse, Tom. J'ai lu la recommandation de Crockford, et bien que cela ait du sens, cela ne va que jusqu'à présent. J'arrive à l'idée - touchée indirectement par doekman - que le vrai pouvoir de with {} est dans la façon dont il peut être utilisé pour manipuler la portée ...
Shog9

2

Je ne vois vraiment pas en quoi l'utilisation de with est plus lisible que de simplement taper object.member. Je ne pense pas que ce soit moins lisible, mais je ne pense pas que ce soit plus lisible non plus.

Comme l'a dit lassevk, je peux certainement voir comment utiliser avec serait plus sujet aux erreurs que d'utiliser simplement la syntaxe très explicite "object.member".


1

Vous avez pu voir la validation d'un formulaire en javascript sur W3schools http://www.w3schools.com/js/js_form_validation.asp où le formulaire d'objet est "scanné" pour trouver une entrée avec le nom "email"

Mais je l'ai modifié pour obtenir de TOUT formulaire tous les champs valident comme non vides, quel que soit le nom ou la quantité de champ dans un formulaire. Eh bien, je n'ai testé que des champs de texte.

Mais le with () a simplifié les choses. Voici le code:

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;
}

1

Le fork Coco de CoffeeScript a un withmot - clé, mais il définit simplement this(également accessible en écriture comme @dans CoffeeScript / Coco) à l'objet cible dans le bloc. Cela supprime l'ambiguïté et atteint la conformité du mode strict ES5:

with long.object.reference
  @a = 'foo'
  bar = @b

0

Voici une bonne utilisation pour with : ajouter de nouveaux éléments à un objet littéral, en fonction des valeurs stockées dans cet objet. Voici un exemple que je viens d'utiliser aujourd'hui:

J'avais un ensemble de tuiles possibles (avec des ouvertures orientées vers le haut, le bas, la gauche ou la droite) qui pouvaient être utilisées, et je voulais un moyen rapide d'ajouter une liste de tuiles qui seraient toujours placées et verrouillées au début du jeu. . Je ne voulais pas continuer à taper types.tbrpour chaque type de la liste, alors je viens de l'utiliser 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");

0

Vous pouvez utiliser avec pour éviter d'avoir à gérer explicitement l'arity lors de l'utilisation de require.js:

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

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

Implémentation 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;
        }
    }   
}

0

Comme Andy E l'a souligné dans les commentaires de la réponse de Shog9, ce comportement potentiellement inattendu se produit lors de l'utilisation withavec un objet littéral:

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]"
  }
}

Pas que ce comportement inattendu ne soit déjà une caractéristique dewith .

Si vous voulez vraiment encore utiliser cette technique, utilisez au moins un objet avec un prototype nul.

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"
  }
}

Mais cela ne fonctionnera que dans ES5 +. N'utilisez pas non plus with.


0

Je travaille sur un projet qui permettra aux utilisateurs de télécharger du code afin de modifier le comportement de certaines parties de l'application. Dans ce scénario, j'ai utilisé une withclause pour empêcher leur code de modifier quoi que ce soit en dehors de la portée avec laquelle je veux qu'ils s'amusent. La partie (simplifiée) du code que j'utilise pour ce faire est:

// 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);

Ce code garantit (quelque peu) que le code défini par l'utilisateur n'a accès à aucun objet de portée globale tel que windowni à aucune de mes variables locales via une fermeture.

Juste pour dire aux sages, je dois encore effectuer des vérifications de code statiques sur le code soumis par l'utilisateur pour s'assurer qu'ils n'utilisent pas d'autres manières sournoises pour accéder à la portée mondiale. Par exemple, le code défini par l'utilisateur suivant accède directement à window:

test = function() {
     return this.window
};
return test();


0

ma

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 résume à

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

Pouvez-vous faire confiance à un code de si faible qualité? Non, nous voyons que cela a été rendu absolument illisible. Cet exemple prouve incontestablement qu'il n'y a pas besoin de with-statement, si je prends bien la lisibilité;)

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.