Gibt es legitime Verwendungszwecke für die "with" -Anweisung von JavaScript?


369

Alan Storms Kommentare als Antwort auf meine Antwort bezüglich der withAussage brachten mich zum Nachdenken. Ich habe selten einen Grund gefunden, diese spezielle Sprachfunktion zu verwenden, und hatte nie viel darüber nachgedacht, wie sie Probleme verursachen könnte. Jetzt bin ich gespannt, wie ich diese effektiv nutzen withund gleichzeitig die Fallstricke vermeiden kann.

Wo haben Sie die withAussage nützlich gefunden?


52
Ich benutze es nie. Es ist einfacher, ohne es zu leben, wenn ich so tue, als ob es nicht existiert.
Nosredna

6
Möglicherweise gab es einmal viele gültige Verwendungen dafür. Aber es ist strittig. ES5 Strict entfernt, withso dass es so etwas nicht mehr gibt.
Thomas Aylott

27
Erwähnenswert ist hier, dass ES5 Strict immer noch optional ist .
Shog9

5
Wäre es nicht besser gewesen, den Standard zu ändern, anstatt 'with' in ES5 strict zu entfernen, sodass jede in 'with' vorgenommene Zuweisung an das Argumentobjekt gebunden ist, wenn keine Variable gefunden wird?
JussiR

2
@JussiR: Wahrscheinlich. Das Problem dabei ist jedoch, dass in älteren Browsern möglicherweise Probleme auftreten.
Sune Rasmussen

Antworten:


520

Mir ist heute eine andere Verwendung eingefallen, daher habe ich das Web aufgeregt durchsucht und eine vorhandene Erwähnung gefunden: Definieren von Variablen innerhalb des Blockbereichs .

Hintergrund

JavaScript legt trotz seiner oberflächlichen Ähnlichkeit mit C und C ++ keine Variablen für den Block fest, in dem sie definiert sind:

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

Das Deklarieren eines Abschlusses in einer Schleife ist eine häufige Aufgabe, bei der dies zu Fehlern führen kann:

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

Da die for-Schleife keinen neuen Bereich einführt, wird derselbe num- mit dem Wert 2- von allen drei Funktionen gemeinsam genutzt.

Ein neuer Anwendungsbereich: letundwith

Mit der Einführung der letAnweisung in ES6 wird es einfach, bei Bedarf einen neuen Geltungsbereich einzuführen, um diese Probleme zu vermeiden:

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

Oder auch:

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

Bis ES6 universell verfügbar ist, bleibt diese Verwendung auf die neuesten Browser und Entwickler beschränkt, die bereit sind, Transpiler zu verwenden. Wir können dieses Verhalten jedoch leicht simulieren, indem wir 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);
   }
}

Die Schleife funktioniert jetzt wie vorgesehen und erstellt drei separate Variablen mit Werten von 0 bis 2. Beachten Sie, dass im Block deklarierte Variablen im Gegensatz zum Verhalten von Blöcken in C ++ keinen Gültigkeitsbereich haben (in C müssen Variablen zu Beginn deklariert werden ein Block, also in gewisser Weise ähnlich). Dieses Verhalten ähnelt einer letBlocksyntax, die in früheren Versionen von Mozilla-Browsern eingeführt wurde, an anderer Stelle jedoch nicht weit verbreitet ist.


15
Nie daran gedacht, mit einem Literal zu verwenden, scheint legitim.
Matt Kantor

81
Das ist wirklich sehr, sehr tot. Ich habe nie daran gedacht, auf diese Weise mit dem Umfang von JavaScript zu spielen. Völlig erweiterte neue Bereiche für meine Codierung. Ich wünschte, ich könnte 10 Mal upvoten!
Kizzx2

27
Für diejenigen, die noch dagegen sind, könnte man immer einen Verschluss gebrauchen:for (var i = 0; i < 3; ++i) { setTimeout ((function () { var num = i; return function () { alert (num); }; }) (), 10);}
Thomas Eding

4
Tatsächlich tritt das oben verlinkte Problem in den meisten Nicht-Mozilla-Browsern (Chrome, Safari, Opera, IE) auf.
Max Shawabkeh

24
lasse Aussage Unterstützung in IE würde meinen Speck spart jetzt wirklich, ich bin zu kämpfen mit meinem Gewissen, ob oder nicht zu verwenden , mit statt. Das eigentliche Problem ist, dass selbst bei einem With als Let noch besondere Vorsicht geboten ist, da die Eigenschaften eines Objekts in der Prototypenkette vererbt wurden. Zum Beispiel var toString = function () { return "Hello"; }; with ({"test":1}) { console.log(toString()); };. Im Bereich der with- Anweisung ist toString () eine geerbte Eigenschaft von Object , sodass die explizit definierte Funktion nicht aufgerufen wird. Trotzdem eine gute Antwort :-)
Andy E

161

Ich habe die with-Anweisung als einfache Form des Bereichsimports verwendet. Angenommen, Sie haben eine Art Markup-Builder. Anstatt zu schreiben:

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

Sie könnten stattdessen schreiben:

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

Für diesen Anwendungsfall mache ich keine Zuweisung, daher habe ich nicht das damit verbundene Mehrdeutigkeitsproblem.


5
So habe ich es in VB gesehen. (Und die einzige Verwendung, die mir bekannt war.)
Mateen Ulhaq

2
Ein Nachteil dabei wäre, dass wenn Sie auf eine Variable innerhalb des with-Blocks verweisen, der sich außerhalb des Markupbuilder-Objekts befindet, die js-Engine ohnehin zuerst innerhalb des Markupbuilder danach sucht, wodurch die Leistung verringert wird.
Adam Thomas

3
Dies hilft wirklich dabei, den Code für diejenigen zu reduzieren, die mit Canvas-Pfaden arbeiten.
Brian McCutchon

4
Die "with" -Version dieses Codes läuft auf meinem Computer buchstäblich mehr als 240 Mal langsamer als die "non-with" -Version desselben. Deshalb sagen die Leute, dass es keine legitime Verwendung dafür gibt. Nicht, weil es den Code an einigen Stellen nicht schöner machen kann. Siehe Benchmark: jsfiddle.net/sc46eeyn
Jimbo Jonny

1
@McBrainy - Das ist genau die Art von Ort, an dem Sie keinen Code verwenden sollten, der um ein Vielfaches langsamer läuft (siehe den Kommentar, den ich gerade über diesem gemacht habe). Wenn Sie Verknüpfungen für super wiederholten Code benötigen, können Sie diese deklarieren. Wenn Sie zum Beispiel context.bezierCurveTohundert Mal hintereinander arbeiten, können Sie sagen var bc2 = context.bezierCurveTo;und dann bc2(x,x,etc);jedes Mal gehen, wenn Sie es aufrufen möchten. Das ist ziemlich schnell und noch weniger ausführlich, während withes sehr langsam ist.
Jimbo Jonny

83

Wie meine vorherigen Kommentare gezeigt haben, glaube ich nicht, dass Sie withsicher verwenden können, egal wie verlockend es in einer bestimmten Situation sein mag. Da das Problem hier nicht direkt behandelt wird, werde ich es wiederholen. Betrachten Sie den folgenden Code

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

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

Ohne sorgfältige Untersuchung dieser Funktionsaufrufe kann der Status Ihres Programms nach Ausführung dieses Codes nicht ermittelt werden. Wenn user.namebereits festgelegt, wird es jetzt sein Bob. Wenn es nicht festgelegt wurde, wird das Global nameinitialisiert oder in geändert, Bobund das userObjekt bleibt ohne nameEigenschaft.

Fehler passieren. Wenn Sie mit verwenden, werden Sie dies schließlich tun und die Wahrscheinlichkeit erhöhen, dass Ihr Programm fehlschlägt. Schlimmer noch, Sie können auf Arbeitscode stoßen, der absichtlich oder durch den Autor, der nichts über diese Eigenart des Konstrukts weiß, einen globalen Code in den with-Block setzt. Es ist fast so, als würde man auf einem Schalter durchfallen. Sie haben keine Ahnung, ob der Autor dies beabsichtigt hat, und es gibt keine Möglichkeit zu wissen, ob das "Korrigieren" des Codes eine Regression einführt.

Moderne Programmiersprachen sind voller Funktionen. Einige Funktionen haben sich nach jahrelangem Gebrauch als schlecht herausgestellt und sollten vermieden werden. Javascript withist einer von ihnen.


18
Dieses Problem tritt nur auf, wenn Sie dem Attribut des Objekts Werte zuweisen. Aber was ist, wenn Sie es nur zum Lesen von Werten verwenden? Ich behaupte, dass es in diesem Fall in Ordnung ist, es zu verwenden.
Airportyh

10
Das gleiche Problem gilt für das Lesen der Werte Toby. Im obigen Code-Snippet wissen Sie nicht, ob für das Benutzerobjekt ein Name festgelegt ist, sodass Sie nicht wissen, ob Sie den globalen Namen oder den Benutzernamen gelesen haben.
Alan Storm

12
Beim Lesen von Werten gibt es eine klare Vorrangregel: Attribute für das Objekt werden vor Variablen außerhalb des Bereichs überprüft. Dies unterscheidet sich nicht von Variablen, die in Funktionen enthalten sind. Das eigentliche Problem bei der Zuweisung und bei 'mit' liegt meines Wissens darin, dass die Frage, ob die Attributzuweisung erfolgt oder nicht, davon abhängt, ob das Attribut für das aktuelle Objekt vorhanden ist, das eine Laufzeit-Eigenschaft ist und nicht einfach abgeleitet werden kann indem Sie sich den Code ansehen.
Airportyh

1
Ich denke du bist vielleicht genau dort, Toby. Das Schreibproblem reicht mir, um das Konstrukt völlig zu scheuen.
Alan Storm

"Es ist fast so, als würde man auf einen Schalter fallen, man hat keine Ahnung ..." - Also lasst uns dann auch switch () verbieten? ;-p
Sz.

66

Ich fand die withAussage in letzter Zeit wirklich unglaublich nützlich. Diese Technik ist mir erst wirklich in den Sinn gekommen, als ich mein aktuelles Projekt gestartet habe - eine in JavaScript geschriebene Befehlszeilenkonsole. Ich habe versucht, die Firebug / WebKit-Konsolen-APIs zu emulieren, bei denen spezielle Befehle in die Konsole eingegeben werden können, die jedoch keine Variablen im globalen Bereich überschreiben. Ich dachte daran, als ich versuchte, ein Problem zu überwinden, das ich in den Kommentaren zu Shog9s ausgezeichneter Antwort erwähnt hatte .

Um diesen Effekt zu erzielen, habe ich zwei with-Anweisungen verwendet, um einen Bereich hinter dem globalen Bereich zu "schichten":

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

Das Tolle an dieser Technik ist, dass sie, abgesehen von den Leistungsnachteilen, nicht die üblichen Befürchtungen der withAussage hat, da wir sowieso im globalen Bereich bewerten - es besteht keine Gefahr, dass Variablen außerhalb unseres Pseudo-Bereichs vorhanden sind geändert.

Ich war inspiriert, diese Antwort zu posten, als ich zu meiner Überraschung die gleiche Technik fand, die an anderer Stelle verwendet wurde - den Chromium-Quellcode !

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

BEARBEITEN: Habe gerade die Firebug-Quelle überprüft, sie verketten 4 mit Anweisungen für noch mehr Ebenen. Verrückt!

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
Aber Sie befürchten, dass ecmascript5 Sie davon abhält. Gibt es eine Ecmascript 5-Lösung?
Kybernetikos

@Adam: Da bin ich mir nicht sicher. ES5 gibt nur im strengen Modus einen Fehler aus. Daher ist dies kein unmittelbares Problem, wenn der strenge Modus nicht global deklariert ist. ES Harmony stellt möglicherweise ein größeres Problem dar, kann jedoch mit einigen neueren Funktionen wie Proxys behoben werden.
Andy E

@AndyE Entschuldigung, das ist kein Thema, aber ist Ihre in JavaScript geschriebene Befehlszeilen-Konsole irgendwo verfügbar?
Kybernetikos

@ Adam: Nein, das ist es nicht. Das Ganze sollte eine Reihe von Entwicklertools für Windows Desktop Gadgets sein, aber ich habe es nie fertiggestellt (obwohl die Konsole sehr gut funktioniert). Ich könnte es irgendwann beenden, obwohl WDGs im Moment keine sehr gute Zukunft haben.
Andy E

3
Vor ein paar Wochen haben wir unsere Konsolenimplementierung in Chrome von Block auf
Symbolmagie verschoben,

54

Ja, ja und ja. Es gibt eine sehr legitime Verwendung. Uhr:

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

Grundsätzlich sind alle anderen DOM- oder CSS-Hooks eine fantastische Verwendung mit. Es ist nicht so, dass "CloneNode" undefiniert ist und zum globalen Bereich zurückkehrt, es sei denn, Sie haben alles getan, um dies zu ermöglichen.

Crockfords Geschwindigkeitsbeschwerde ist, dass durch mit ein neuer Kontext erstellt wird. Kontexte sind im Allgemeinen teuer. Genau. Wenn Sie jedoch gerade ein div erstellt haben und kein Framework zum Festlegen Ihres CSS zur Verfügung haben und etwa 15 CSS-Eigenschaften manuell einrichten müssen, ist das Erstellen eines Kontexts wahrscheinlich billiger als das Erstellen von Variablen und 15 Dereferenzen:

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

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

usw...


5
+1, da ich auch denke, dass es viele legitime Verwendungen von gibt with. In diesem speziellen Fall können Sie jedoch einfach element.style.cssText="background: black ; color: blue ; border: 1px solid green"
Folgendes

5
Sie können dasselbe in einer Zeile mit der einfachen extendMethode von jQuery oder Underscore.js erreichen : $.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'}).
Trevor Burnham

9
@ TrevorBurnham - Wenn Sie davon ausgehen wollen, dass jQuery verfügbar ist, verwenden Sie einfach seine .css()Methode ...
nnnnnn

4
Was genau verhindert, dass diese Variablen in den globalen Bereich gelangen? Liegt es nur daran, dass alle CSS-Stile immer für alle Elemente definiert sind, oder was?
Mpen

1
@Mark ja, sie werden immer definiert, mit Nullen oder leeren Zeichenfolgen als Werten, wenn es keinen benutzerdefinierten Stil für eine Eigenschaft gibt
Esailija

34

Sie können eine kleine Hilfsfunktion definieren, um die Vorteile withohne Mehrdeutigkeit zu nutzen:

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

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

8
Oh mein Gott, mein Kopf explodiert! ohne die Mehrdeutigkeit? Muss das abstimmen, Mann!
Jarrod Dixon

@ Jarrod: Was ist daran so lustig ( is.gd/ktoZ )? Fast jeder, der diese Seite nutzt, ist schlauer als ich. Verzeihen Sie mir, wenn ich falsch liege, aber dies scheint eine schlechte Information zu sein.
Rabe

14
Aber das ist nur länger und schwerer zu verstehen: var _ = obj_name_here; _.a = "foo"; _.b = "bar;
Rene Saarsoo

3
Rene: Damit setzen Sie die Variable "_" dem äußeren Bereich aus, was zu potenziellen Fehlern führt. Außerdem werden alle temporären Variablen verfügbar gemacht, die bei der Berechnung von Objektparametern verwendet werden.
John Millikin

30
Sie werden mehr Fehler bekommen, with_wenn Sie eine schlammige Doppelversion von sind (function(_){ _.a="foo"; })(object_here);(die Standardmethode zum Simulieren von Blöcken im C / Java-Stil). Verwenden Sie das stattdessen.
mk.


18

Ich benutze es nie mit, sehe keinen Grund dafür und empfehle es nicht.

Das Problem dabei withist, dass es zahlreiche lexikalische Optimierungen verhindert, die eine ECMAScript-Implementierung durchführen kann. Angesichts des Aufstiegs schneller JIT-basierter Motoren wird dieses Problem in naher Zukunft wahrscheinlich noch wichtiger.

Es sieht vielleicht so aus, als ob withes sauberere Konstrukte zulässt (wenn beispielsweise ein neuer Bereich anstelle eines gemeinsamen anonymen Funktionsumschlags eingeführt oder ausführliches Aliasing ersetzt wird), aber es lohnt sich wirklich nicht . Neben einer verminderten Leistung besteht immer die Gefahr, einer Eigenschaft eines falschen Objekts zuzuweisen (wenn eine Eigenschaft für ein Objekt im injizierten Bereich nicht gefunden wird) und möglicherweise fälschlicherweise globale Variablen einzuführen. IIRC, letzteres Problem hat Crockford dazu motiviert, zu empfehlen, es zu vermeiden with.


6
Der Performance-Bogeyman wird häufig ausgetrottet, fast so oft wie die globale Sache ... Kommt mir immer seltsam vor, da es sich um JavaScript handelt, über das wir sprechen. Sie würden annehmen, dass der Leistungseinbruch wirklich dramatisch ist , um so viel Aufmerksamkeit zu verdienen, aber ... Wenn Sie harte Zahlen zu den Kosten von with(){}Konstrukten haben, wie sie in anderen Antworten hier in modernen Browsern angegeben sind, würde ich gerne sehen Sie!
Shog9

6
Warum ist es im Zusammenhang mit Javascript seltsam? :) Und ja, es ist dramatisch. Denken Sie darüber nach - eine Implementierung muss einen Ausdruck in Klammern auswerten, ihn in ein Objekt konvertieren, ihn in die Vorderseite der aktuellen Bereichskette einfügen, die Anweisung innerhalb des Blocks auswerten und dann die Bereichskette wieder normalisieren. Das ist viel Arbeit. Viel mehr als eine einfache Eigenschaftssuche, die in einen hochoptimierten Low-Level-Code umgewandelt werden kann. Hier ist ein sehr einfacher Benchmark, den ich gerade gemacht habe (lassen Sie mich wissen, wenn Sie Fehler finden), der den Unterschied zeigt - gist.github.com/c36ea485926806020024
kangax

5
@kangax: Ich komme aus einem C ++ - Hintergrund, in dem es für viele Programmierer traditionell ist, von kleinen Effizienzgewinnen in ihrem Code besessen zu sein, selbst wenn sie keinen spürbaren Einfluss auf die Leistung der größeren Routine oder des größeren Programms haben. Es erscheint mir im Kontext von JavaScript seltsam, wo ein so großer Teil der Leistung einer Routine von der VM-Implementierung abhängen kann. Ich habe einige Fälle gesehen, in denen JS-Programmierer beispielsweise eine anonyme Funktion aufgrund von Bedenken hinsichtlich der Einrichtungskosten vermeiden, aber dies scheint die Ausnahme und nicht die Regel zu sein, die für sehr sensible Codebereiche reserviert ist.
Shog9

5
Das heißt, Sie haben absolut Recht mit den Kosten für with(){}: Das Einrichten eines neuen Bereichs mit withist für jeden von mir getesteten Browser enorm teuer. Sie möchten dies in jedem Code vermeiden, der sehr häufig aufgerufen wird. Darüber hinaus zeigte Chrome einen dramatischen Erfolg für jeden Code, der innerhalb eines with()Bereichs ausgeführt wird. Interessanterweise hatte der IE die besten Leistungsmerkmale für Code innerhalb von with()Blöcken: Das Ausklammern der Einrichtungskosten with()bietet die schnellste Möglichkeit für den Mitgliederzugriff in IE6- und IE8-VMs (obwohl diese VMs insgesamt die langsamsten sind). Gutes Zeug, danke ...
Shog9

5
FWIW: Hier sind die gleichen Tests mit bereinigten Einrichtungskosten: jsbin.com/imidu/edit Der variable Zugriff für with()ist in Chrome fast eine Größenordnung langsamer und im IE mehr als doppelt so schnell ...!
Shog9

13

Visual Basic.NET hat eine ähnliche WithAnweisung. Eine der gebräuchlichsten Methoden ist das schnelle Festlegen einer Reihe von Eigenschaften. Anstatt:

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

, Ich kann schreiben:

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

Dies ist nicht nur eine Frage der Faulheit. Es sorgt auch für viel besser lesbaren Code. Und im Gegensatz zu JavaScript weist es keine Mehrdeutigkeit auf, da Sie allen von der Anweisung betroffenen .Punkten einen (Punkt) voranstellen müssen . Die folgenden beiden unterscheiden sich also deutlich:

With someObject
    .Foo = ''
End With

vs.

With someObject
    Foo = ''
End With

Ersteres ist someObject.Foo; Letzteres liegt Fooim Geltungsbereich außerhalb someObject .

Ich finde, dass die mangelnde Unterscheidung von JavaScript weitaus weniger nützlich ist als die Variante von Visual Basic, da das Risiko von Mehrdeutigkeiten zu hoch ist. Davon abgesehen withist es immer noch eine leistungsstarke Idee, die zu einer besseren Lesbarkeit führen kann.


2
Wahr genug. Beantwortet seine Frage jedoch nicht. Es ist also kein Thema.
Allain Lalonde

6
Das ging mir auch durch den Kopf. jemand musste es sagen . Warum kann JavaScript nicht auch nur den Punkt haben?
Carson Myers

Einverstanden, die Punktnotation ist überlegen, wünschte, JavaScript verwendet es. +1


7

Die Verwendung von "mit" kann Ihren Code trockener machen.

Betrachten Sie den folgenden Code:

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

Sie können es wie folgt trocknen:

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

Ich denke, es hängt davon ab, ob Sie Lesbarkeit oder Ausdruckskraft bevorzugen.

Das erste Beispiel ist besser lesbar und wird wahrscheinlich für den meisten Code empfohlen. Aber der meiste Code ist sowieso ziemlich zahm. Die zweite ist etwas dunkler, verwendet jedoch die Ausdruckskraft der Sprache, um die Codegröße und überflüssige Variablen zu reduzieren.

Ich stelle mir vor, dass Leute, die Java oder C # mögen, den ersten Weg wählen würden (object.member) und diejenigen, die Ruby oder Python bevorzugen, den letzteren wählen würden.


Hoppla, ich habe nicht bemerkt, dass jemand bereits vor einem Jahr im Grunde das gleiche Beispiel gepostet hat. Abgesehen von Leistungsproblemen sorgt "with" für netten DRY-Code auf Kosten der etwas schwierigeren Lesbarkeit. Ich denke, dass es für die Zusammenarbeit mit anderen Entwicklern oder den meisten Produktionscodes eine gute Praxis ist, das Schlüsselwort "with" zu vermeiden. Wenn Sie jedoch mit Programmierern auf Expertenebene zusammenarbeiten und verstehen, wie potenzielle Ineffizienzen vermieden werden können, gehen Sie auf jeden Fall mit "mit" in die Stadt.
Jonah

6

Ich denke, die offensichtliche Verwendung ist als Abkürzung. Wenn Sie beispielsweise ein Objekt initialisieren, speichern Sie einfach die Eingabe von "ObjectName". Ein bisschen wie Lisp's "With-Slots", mit denen Sie schreiben können

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

Das ist das gleiche wie schreiben

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

Es ist offensichtlicher, warum dies eine Verknüpfung ist, als wenn Ihre Sprache "Objectname.foo" zulässt, aber immer noch.


1
toll, lisp code zu sehen! Ich denke, "mit" in Javascript ist offensichtlich von den Schemawurzeln als Sprache inspiriert, aber leider werden Sie für das Posten von LISP zu einer Javascript-Frage abgelehnt.
Feuer Crow

1
Upvoting nach Grundprinzip. 'with' ist ein phänomenal mächtiges Konstrukt. Aber die meisten JS-Leute verstehen Schließungen nicht und schreiben lächerlich komplexe Vererbungssysteme für Java-Klassen zusätzlich zu JS. Wie können sie sich also des Metaprogrammierungspotenzials bewusst werden, das "mit" bietet?
Jared

Natürlich with-slotsmüssen Sie angeben, welche Slots Sie verwenden, während Sie withalle Slots verwenden, die zur Laufzeit gebunden sind.
Samuel Edwin Ward

6

Aufgrund meiner Erfahrung mit Delphi würde ich sagen, dass die Verwendung mit eine letzte Größenoptimierung sein sollte, die möglicherweise von einer Art Javascript-Minimierungsalgorithmus mit Zugriff auf statische Code-Analyse durchgeführt wird, um die Sicherheit zu überprüfen.

Die Scoping-Probleme, auf die Sie bei der liberalen Verwendung der with- Anweisung stoßen können, können ein königlicher Schmerz in der a ** sein, und ich möchte nicht, dass jemand eine Debugging-Sitzung durchläuft, um herauszufinden, was in Ihrem Code vor sich geht , nur um herauszufinden, dass ein Objektelement oder die falsche lokale Variable anstelle Ihrer von Ihnen beabsichtigten globalen oder äußeren Bereichsvariablen erfasst wurde.

Der VB mit Aussage ist insofern besser, als es die Punkte benötigt, um das Scoping zu disambiguieren, aber das Delphi mit Aussage ist eine geladene Waffe mit einem Haarauslöser, und es sieht für mich so aus, als ob das Javascript ähnlich genug ist, um dieselbe Warnung zu rechtfertigen.


5
Das Javascript mit Aussage ist schlechter als das Delphi. In Delphi ist with genauso schnell (wenn nicht sogar schneller) als die object.member-Notation. In Javascript muss with den Bereich durchlaufen, um nach übereinstimmenden Mitgliedern zu suchen, wodurch es immer langsamer als die object.member-Notation wird.
Martijn

5

Die Verwendung mit wird nicht empfohlen und ist im strengen ECMAScript 5-Modus verboten. Die empfohlene Alternative besteht darin, das Objekt, auf dessen Eigenschaften Sie zugreifen möchten, einer temporären Variablen zuzuweisen.

Quelle: Mozilla.org


4

Die with-Anweisung kann zum Verringern der Codegröße oder für private Klassenmitglieder verwendet werden. Beispiel:

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

Die with-Anweisung ist sehr nützlich, wenn Sie den Bereich ändern möchten. Dies ist erforderlich, um einen eigenen globalen Bereich zu haben, den Sie zur Laufzeit bearbeiten können. Sie können Konstanten oder bestimmte Hilfsfunktionen einfügen, die häufig verwendet werden, z. B. "toUpper", "toLower" oder "isNumber", "clipNumber".

Über die schlechte Leistung habe ich so oft gelesen: Das Scoping einer Funktion hat keinen Einfluss auf die Leistung. In meinem FF läuft eine Scoped-Funktion schneller als eine nicht-Scope:

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

Auf die oben erwähnte Weise hat die with-Anweisung keine negativen Auswirkungen auf die Leistung, aber eine gute, da sie die Codegröße verringert, was sich auf die Speichernutzung auf Mobilgeräten auswirkt.


3

Durch die Verwendung von with wird Ihr Code in vielen Implementierungen langsamer, da jetzt alles in einen zusätzlichen Bereich für die Suche eingeschlossen wird. Es gibt keinen legitimen Grund für die Verwendung in JavaScript.


5
Vorzeitige Optimierung. Behaupten Sie nicht "langsamer", es sei denn, Sie haben die Zahlen geknackt. Jeder Overhead ist sowohl für moderne als auch für alte Geräte wahrscheinlich trivial.
mk.

2
Ich bin mit Ihrer Schlussfolgerung überhaupt nicht einverstanden, da sie für andere Entwickler als Sie möglicherweise kein Problem darstellt.
Dave Van den Eynde

4
@mk: ok, hier knirscht die Zahl für dich: 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;gibt im Durchschnitt 2500, 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;gibt im Durchschnitt 750, was die Verwendung über 3-mal langsamer macht.
Yorick

3
Ich habe diese gerade in Chrome 23 in der Konsole ausgeführt, als ich das sah. Die Ergebnisse, die ich bekam, waren 1138 für den withCode und 903 ohne. Mit diesem winzigen Unterschied würde ich selbst in einer engen Schleife eine Auswahl treffen, die auf der Einfachheit der Codierung und der einfachen Umgestaltung von Fall zu Fall basiert, bevor ich mich um die Leistung kümmere.
Plynx

3

Ich denke, die with-Anweisung kann nützlich sein, wenn eine Vorlagensprache in JavaScript konvertiert wird. Zum Beispiel JST in base2 , aber ich habe es öfter gesehen.

Ich bin damit einverstanden, dass man dies ohne die with-Anweisung programmieren kann. Aber weil es keine Probleme gibt, ist es eine legitime Verwendung.


3

Es ist gut, um Code, der in einer relativ komplizierten Umgebung ausgeführt wird, in einen Container einzufügen: Ich verwende ihn, um eine lokale Bindung für "Fenster" zu erstellen und um Code für einen Webbrowser auszuführen.


3

Ich denke, die wörtliche Verwendung des Objekts ist interessant, wie ein Ersatz für die Verwendung eines Verschlusses

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

oder die mit Aussage gleichbedeutend mit einer Schließung

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

Ich denke, das eigentliche Risiko besteht darin, versehentlich Variablen zu minimieren, die nicht Teil der with-Anweisung sind. Deshalb gefällt mir das Objektliteral, mit dem übergeben wird. Sie können genau sehen, wie es im hinzugefügten Kontext im Code aussehen wird.


3

Ich habe eine "Zusammenführungs" -Funktion erstellt, die einige dieser Unklarheiten mit der withAnweisung beseitigt :

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

Ich kann es ähnlich wie verwenden with, aber ich kann wissen, dass es keinen Bereich beeinflusst, den ich nicht beeinflussen möchte.

Verwendungszweck:

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

Für einige kurzen Codestücke, würde Ich mag die trigonometrischen Funktionen wie verwenden sin, cosusw. in Grad - Modus statt im Strahlungsmodus. Zu diesem Zweck verwende ich ein AngularDegreeObjekt:

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

Dann kann ich die trigonometrischen Funktionen im Gradmodus ohne weiteres Sprachrauschen in einem withBlock verwenden:

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

Dies bedeutet: Ich verwende ein Objekt als Sammlung von Funktionen, die ich in einem begrenzten Codebereich für den direkten Zugriff aktiviere. Ich finde das nützlich.


Es ist keine gute Idee, die withAnweisung auf diese Weise zu verwenden. Sie macht den Code nur schwer lesbar, da Sie nicht wissen, welche Funktion global ist und welche Funktion im Bereich von with object aufgerufen wird Objektbereich dann wird es versuchen, im globalen Namespace darauf zuzugreifen
Saket Patel

Da ich mir des Scoping-Problems bewusst bin, finde ich dies immer noch nützlich. Ein Mathematiker, der den Code liest, möchte direkt sehen, dass die obige Formel eine Anwendung des "Gesetzes der 4 aufeinanderfolgenden Teile" in der sphärischen Trigonometrie ist. Die strenge Alternative verschleiert die Formel: 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;würde das gleiche Ergebnis liefern, aber es ist der Horror.
Rplantiko

@rplantiko Die Sache zu beachten ist, dass die meisten Menschen sich damit nicht wohl fühlen. Es sei denn, Sie schreiben Code, den sonst niemand berühren wird. Ich bin mir auch ziemlich sicher, dass ich Ihnen ein paar Verwendungen zeigen kann, die Sie überraschen withwürden.
Juan Mendes

2

Ich denke, dass der Nutzen von withdavon abhängen kann, wie gut Ihr Code geschrieben ist. Wenn Sie beispielsweise Code schreiben, der folgendermaßen aussieht:

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

dann könnte man das argumentieren with die Lesbarkeit des Codes verbessert:

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

Umgekehrt könnte argumentiert werden, dass Sie gegen das Gesetz von Demeter verstoßen , aber andererseits vielleicht auch nicht. Ich schweife ab =).

Beachten Sie vor allem, dass Douglas Crockford die Verwendung nicht empfiehlt with. Ich fordere Sie auf, seinen Blog-Beitrag withund seine Alternativen hier zu lesen .


Danke für die Antwort, Tom. Ich habe Crockfords Empfehlung gelesen, und obwohl es Sinn macht, geht es nur so weit. Ich komme zu der Idee - indirekt von Doekman berührt -, dass die wahre Kraft von with {} in der Art und Weise liegt, wie es verwendet werden kann, um den Bereich zu manipulieren ...
Shog9

2

Ich sehe nur wirklich nicht, wie die Verwendung von with besser lesbar ist als nur die Eingabe von object.member. Ich denke nicht, dass es weniger lesbar ist, aber ich denke auch nicht, dass es besser lesbar ist.

Wie lassevk sagte, kann ich definitiv sehen, dass die Verwendung mit fehleranfälliger wäre als nur die sehr explizite "object.member" -Syntax.


1

Die Validierung eines Formulars in Javascript finden Sie unter W3schools http://www.w3schools.com/js/js_form_validation.asp. Dort wird das Objektformular "gescannt", um eine Eingabe mit dem Namen "email" zu finden.

Aber ich habe es so geändert, dass von JEDEM Formular alle Felder als nicht leer validiert werden, unabhängig vom Namen oder der Anzahl der Felder in einem Formular. Nun, ich habe nur Textfelder getestet.

Aber das with () hat die Sache einfacher gemacht. Hier ist der 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

Die Coco- Gabel von CoffeeScript hat ein withSchlüsselwort, setzt jedoch einfach this(auch beschreibbar wie @in CoffeeScript / Coco) das Zielobjekt innerhalb des Blocks. Dies beseitigt Mehrdeutigkeiten und erreicht die strikte Einhaltung des ES5-Modus:

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

0

Hier ist eine gute Verwendung für with : Hinzufügen neuer Elemente zu einem Objektliteral, basierend auf den in diesem Objekt gespeicherten Werten. Hier ist ein Beispiel, das ich heute verwendet habe:

Ich hatte eine Reihe möglicher Kacheln (mit Öffnungen nach oben, unten, links oder rechts), die verwendet werden konnten, und ich wollte schnell eine Liste von Kacheln hinzufügen, die zu Beginn des Spiels immer platziert und gesperrt wurden . Ich wollte nicht types.tbrfür jeden Typ in der Liste weiter tippen, also habe ich nur verwendet 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

Sie können mit verwenden, um zu vermeiden, dass Sie arity explizit verwalten müssen, wenn Sie require.js verwenden:

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

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

Implementierung von 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

Wie Andy E in den Kommentaren der Antwort von Shog9 hervorhob, tritt dieses möglicherweise unerwartete Verhalten bei der Verwendung withmit einem Objektliteral auf:

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

Nicht, dass unerwartetes Verhalten nicht schon ein Markenzeichen gewesen wärewith .

Wenn Sie diese Technik wirklich noch verwenden möchten, verwenden Sie mindestens ein Objekt mit einem Null-Prototyp.

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

Dies funktioniert jedoch nur in ES5 +. Auch nicht benutzen with.


0

Ich arbeite an einem Projekt, mit dem Benutzer Code hochladen können, um das Verhalten von Teilen der Anwendung zu ändern. In diesem Szenario habe ich eine withKlausel verwendet, um zu verhindern, dass der Code irgendetwas außerhalb des Bereichs ändert, mit dem sie herumspielen sollen. Der (vereinfachte) Teil des Codes, den ich dazu benutze, ist:

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

Dieser Code stellt (etwas) sicher, dass der benutzerdefinierte Code weder Zugriff auf Objekte mit globalem Gültigkeitsbereich hat, wie z window durch einen Abschluss auf eine meiner lokalen Variablen hat.

Nur um es weise zu sagen, ich muss immer noch statische Codeprüfungen für den vom Benutzer eingereichten Code durchführen, um sicherzustellen, dass sie keine anderen hinterhältigen Methoden verwenden, um auf den globalen Bereich zuzugreifen. Der folgende benutzerdefinierte Code bietet beispielsweise direkten Zugriff auf window:

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


0

Meine

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
}

läuft darauf hinaus

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

Können Sie so minderwertigem Code vertrauen? Nein, wir sehen, dass es absolut unlesbar gemacht wurde. Dieses Beispiel beweist unbestreitbar, dass keine With-Statement erforderlich ist, wenn ich die Lesbarkeit richtig nehme;)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.