388 Stimmen

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

Alan Storm's Kommentare als Antwort auf meine Antwort bezüglich der with Anweisung hat mich zum Nachdenken gebracht. Ich habe selten einen Grund gefunden, dieses besondere Sprachmerkmal zu verwenden, und ich habe nie darüber nachgedacht, wie es Probleme verursachen könnte. Jetzt bin ich neugierig, wie ich diese Funktion effektiv nutzen kann with und vermeidet gleichzeitig ihre Fallstricke.

Wo haben Sie die with Aussage nützlich?

534voto

Shog9 Punkte 151504

Heute kam mir eine weitere Verwendung in den Sinn, also suchte ich aufgeregt im Internet und fand eine bereits vorhandene Erwähnung davon: Definieren von Variablen im Blockbereich .

Hintergrund

Trotz seiner oberflächlichen Ähnlichkeit mit C und C++ beschränkt JavaScript Variablen nicht auf den Block, in dem sie definiert sind:

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

Die Deklaration 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 Anwendungsbereich einführt, wird derselbe num - mit einem Wert von 2 - wird von allen drei Funktionen gemeinsam genutzt.

Ein neuer Anwendungsbereich: let y with

Mit der Einführung des let Anweisung in ES6 wird es einfach, einen neuen Anwendungsbereich einzuführen, wenn dies notwendig ist, 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);
}

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

Bis ES6 allgemein 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 nun wie vorgesehen und erzeugt drei separate Variablen mit Werten von 0 bis 2. Beachten Sie, dass Variablen, die als innerhalb im Gegensatz zum Verhalten von Blöcken in C++ (in C müssen Variablen am Anfang eines Blocks deklariert werden, so dass es in gewisser Weise ähnlich ist), nicht auf den Block bezogen sind. Dieses Verhalten ist eigentlich ziemlich ähnlich zu einem let Blocksyntax die in früheren Versionen von Mozilla-Browsern eingeführt wurde, aber anderswo nicht weit verbreitet ist.

165voto

airportyh Punkte 20678

Ich habe die with-Anweisung als eine einfache Form des scoped import verwendet. Nehmen wir an, Sie haben eine Art von 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')
    )
  )
}

In diesem Anwendungsfall nehme ich keine Zuweisung vor, so dass ich das damit verbundene Problem der Mehrdeutigkeit nicht habe.

87voto

Alan Storm Punkte 160579

Wie in meinen vorherigen Kommentaren angedeutet, glaube ich nicht, dass Sie die with sicher, egal wie verlockend es in einer bestimmten Situation auch sein mag. Da das Thema hier nicht direkt behandelt wird, wiederhole ich es. Betrachten Sie den folgenden Code

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

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

Ohne eine sorgfältige Untersuchung dieser Funktionsaufrufe lässt sich nicht feststellen, wie der Zustand Ihres Programms nach der Ausführung dieses Codes sein wird. Wenn user.name bereits eingestellt war, wird sie nun Bob . Wenn sie nicht gesetzt wurde, wird die globale name wird initialisiert oder geändert in Bob und die user Objekt bleibt ohne einen name Eigentum.

Bugs passieren. Wenn Sie mit werden Sie dies letztendlich tun und die Wahrscheinlichkeit erhöhen, dass Ihr Programm scheitert. Schlimmer noch, Sie könnten auf funktionierenden Code stoßen, der eine globale Variable im with-Block setzt, entweder absichtlich oder weil der Autor diese Eigenart des Konstrukts nicht kennt. Es ist ähnlich wie bei einem Fall-Through-Switch: Sie wissen nicht, ob der Autor dies beabsichtigt hat, und können nicht wissen, ob die "Korrektur" des Codes eine Regression einleiten wird.

Moderne Programmiersprachen sind vollgestopft mit Funktionen. Einige Funktionen haben sich nach jahrelangem Gebrauch als schlecht herausgestellt und sollten vermieden werden. Javascript's with ist einer von ihnen.

71voto

Andy E Punkte 324972

Ich fand die with Erklärung in letzter Zeit als unglaublich nützlich erwiesen. Diese Technik kam mir nie wirklich in den Sinn, bis ich mein aktuelles Projekt begann - 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 aber keine Variablen im globalen Bereich überschreiben. Ich kam auf diese Idee, als ich versuchte, ein Problem zu lösen, das ich in den Kommentaren zu Shog9s ausgezeichnete Antwort .

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 unter den üblichen Ängsten vor dem with Anweisung, da wir ohnehin im globalen Bereich evaluieren - es besteht keine Gefahr, dass Variablen außerhalb unseres Pseudobereichs geändert werden.

Ich wurde zu dieser Antwort inspiriert, als ich zu meiner Überraschung die gleiche Technik an anderer Stelle fand - die 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);
}

EDITAR: Ich habe gerade den Firebug-Quellcode überprüft, sie Kette 4 mit Aussagen zusammen für noch mehr Schichten. 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);" +
    "}" +
"}}}}";

58voto

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

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

Im Grunde genommen sind alle anderen DOM- oder CSS-Hooks fantastische Verwendungen von with. Es ist nicht wie "CloneNode" wird undefiniert und gehen zurück in den globalen Bereich, es sei denn, Sie ging aus dem Weg und beschlossen, es möglich zu machen.

Crockford beschwert sich über die Geschwindigkeit, mit der ein neuer Kontext geschaffen wird. Kontexte sind im Allgemeinen teuer. Dem stimme ich zu. Aber wenn Sie gerade ein div erstellt haben und kein Framework zur Hand haben, um Ihr CSS einzustellen, und 15 oder so CSS-Eigenschaften von Hand einrichten müssen, dann 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...

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X