wake-up-neo.com

Warum sollten Sie in JavaScript Inkrementierungs- ("++") und Dekrementierungsoperatoren ("-") vermeiden?

Eines der Tipps für das jslint-Tool lautet:

++ und -
Es ist bekannt, dass die Operatoren ++ (Inkrementieren) und - (Dekrementieren) zu fehlerhaftem Code beitragen, indem sie zu übermäßiger List ermutigen. Sie sind nach der fehlerhaften Architektur die zweitwichtigsten, wenn es darum geht, Viren und andere Sicherheitsbedrohungen abzuwehren. Es gibt eine Plus-Plus-Option, die die Verwendung dieser Operatoren verbietet.

Ich weiß, dass PHP Konstrukte wie _$foo[$bar++]_ leicht zu einseitigen Fehlern führen können, aber ich konnte keinen besseren Weg finden, die Schleife zu steuern, als ein while( a < 10 ) do { /* foo */ a++; } oder for (var i=0; i<10; i++) { /* foo */ }.

Markiert das Jslint sie, weil es einige ähnliche Sprachen gibt, denen die Syntax "_++_" und "_--_" fehlt, oder behandelt es sie anders, oder gibt es andere Gründe, um "_++_" und "zu vermeiden? "_--_", dass ich fehlen könnte?

345
artlung

Meine Ansicht ist, immer ++ und - für sich allein in einer einzigen Zeile zu verwenden, wie in:

i++;
array[i] = foo;

anstatt von

array[++i] = foo;

Alles darüber hinaus kann für einige Programmierer verwirrend sein und ist es meiner Ansicht nach einfach nicht wert. For-Schleifen sind eine Ausnahme, da die Verwendung des Inkrement-Operators idiomatisch und damit immer klar ist.

395
cdmckay

Ich bin ehrlich gesagt durch diesen Rat verwirrt. Ein Teil von mir fragt sich, ob es mehr mit mangelnder (wahrgenommener oder tatsächlicher) Erfahrung mit Javascript-Codierern zu tun hat.

Ich kann sehen, wie jemand, der nur an einem Beispielcode "hackt", einen unschuldigen Fehler mit ++ und - machen kann, aber ich verstehe nicht, warum ein erfahrener Fachmann dies vermeiden würde.

249
Jon B

Es gibt eine Geschichte in C, Dinge zu tun wie:

while (*a++ = *b++);

um eine Zeichenfolge zu kopieren, ist dies möglicherweise die Quelle der übermäßigen Trickserei, auf die er sich bezieht.

Und es gibt immer die Frage nach was

++i = i++;

oder

i = i++ + ++i;

eigentlich tun. Es ist in einigen Sprachen definiert und in anderen gibt es keine Garantie dafür, was passieren wird.

Abgesehen von diesen Beispielen gibt es meines Erachtens nichts Idiomatischeres als eine for-Schleife, die ++ Zum Inkrementieren verwendet. In einigen Fällen könnten Sie mit einer foreach-Schleife oder einer while-Schleife davonkommen, die einen anderen Zustand überprüft. Es ist jedoch lächerlich, Ihren Code zu verzerren, um eine Inkrementierung zu vermeiden.

69
Eclipse

Wenn Sie JavaScript The Good Parts lesen, werden Sie feststellen, dass Crockfords Ersatz für i ++ in einer for -Schleife i + = 1 ist (nicht i = i + 1). Das ist ziemlich sauber und lesbar und es ist weniger wahrscheinlich, dass es sich in etwas "heikles" verwandelt.

Crockford hat das Deaktivieren von Autoincrement und Autodecrement zu einer Option in jsLint gemacht. Sie entscheiden, ob Sie dem Rat folgen möchten oder nicht.

Meine persönliche Regel ist, nichts in Kombination mit Autoincrement oder Autodecrement zu tun.

Ich habe aus jahrelanger Erfahrung in C gelernt, dass ich keine Pufferüberläufe (oder Array-Indizes außerhalb der Grenzen) bekomme, wenn ich die Verwendung einfach halte. Aber ich habe herausgefunden, dass ich Pufferüberläufe bekomme, wenn ich in die "übermäßig knifflige" Praxis falle, andere Dinge in der gleichen Anweisung zu tun.

Für meine eigenen Regeln ist die Verwendung von i ++ als Inkrement in einer for -Schleife in Ordnung.

45
Nosredna

In einer Schleife ist es harmlos, aber in einer Zuweisungsanweisung kann es zu unerwarteten Ergebnissen führen:

var x = 5;
var y = x++; // y is now 5 and x is 6
var z = ++x; // z is now 7 and x is 7

Leerzeichen zwischen der Variablen und dem Operator können ebenfalls zu unerwarteten Ergebnissen führen:

a = b = c = 1; a ++ ; b -- ; c; console.log('a:', a, 'b:', b, 'c:', c)

Bei einem Abschluss können auch unerwartete Ergebnisse ein Problem sein:

var foobar = function(i){var count = count || i; return function(){return count++;}}

baz = foobar(1);
baz(); //1
baz(); //2


var alphabeta = function(i){var count = count || i; return function(){return ++count;}}

omega = alphabeta(1);
omega(); //2
omega(); //3

Und es löst das automatische Einfügen von Semikolons nach einer neuen Zeile aus:

var foo = 1, bar = 2, baz = 3, alpha = 4, beta = 5, delta = alpha
++beta; //delta is 4, alpha is 4, beta is 6

verwirrung vor und nach dem Inkrementieren kann zu einzelnen Fehlern führen, die äußerst schwierig zu diagnostizieren sind. Zum Glück sind sie auch komplett überflüssig. Es gibt bessere Möglichkeiten, einer Variablen eine 1 hinzuzufügen.

Referenzen

26
Paul Sweatte

Betrachten Sie den folgenden Code

    int a[10];
    a[0] = 0;
    a[1] = 0;
    a[2] = 0;
    a[3] = 0;
    int i = 0;
    a[i++] = i++;
    a[i++] = i++;
    a[i++] = i++;

da i ++ zweimal ausgewertet wird, ist die Ausgabe (vom vs2005-Debugger)

    [0] 0   int
    [1] 0   int
    [2] 2   int
    [3] 0   int
    [4] 4   int

Betrachten Sie nun den folgenden Code:

    int a[10];
    a[0] = 0;
    a[1] = 0;
    a[2] = 0;
    a[3] = 0;
    int i = 0;
    a[++i] = ++i;
    a[++i] = ++i;
    a[++i] = ++i;

Beachten Sie, dass die Ausgabe identisch ist. Jetzt könnte man denken, dass ++ i und i ++ gleich sind. Sie sind nicht

    [0] 0   int
    [1] 0   int
    [2] 2   int
    [3] 0   int
    [4] 4   int

Betrachten Sie abschließend diesen Code

    int a[10];
    a[0] = 0;
    a[1] = 0;
    a[2] = 0;
    a[3] = 0;
    int i = 0;
    a[++i] = i++;
    a[++i] = i++;
    a[++i] = i++;

Die Ausgabe ist jetzt:

    [0] 0   int
    [1] 1   int
    [2] 0   int
    [3] 3   int
    [4] 0   int
    [5] 5   int

Sie sind also nicht dasselbe, beide zu mischen, führt zu weniger intuitivem Verhalten. Ich denke, dass for-Schleifen mit ++ in Ordnung sind, aber achten Sie darauf, wenn Sie mehrere ++ - Symbole in derselben Zeile oder in derselben Anweisung haben

17
Eric

Die "Vor-" und "Nach" -Natur von Inkrementierungs- und Dekrementierungsoperatoren kann für diejenigen verwirrend sein, die mit ihnen nicht vertraut sind. Auf diese Weise können sie schwierig sein.

16
Paul Sonier

Meiner Ansicht nach ist "Explizit ist immer besser als implizit." Weil Sie irgendwann mit dieser inkrementellen Anweisung verwechselt werden könnten y+ = x++ + ++y. Ein guter Programmierer macht seinen Code immer lesbarer.

16
Sriram

Das wichtigste Argument, um ++ oder - zu vermeiden, besteht darin, dass die Operatoren Werte zurückgeben und gleichzeitig Nebenwirkungen verursachen, was es schwieriger macht, über den Code nachzudenken.

Aus Gründen der Effizienz bevorzuge ich:

  • ++ i when wird nicht verwendet der Rückgabewert (nicht temporär)
  • i ++ when sing der Rückgabewert (kein Pipeline-Stillstand)

Ich bin ein Fan von Mr. Crockford, aber in diesem Fall muss ich nicht zustimmen. ++i ist 25% weniger zu analysierender Text als i+=1 und wohl deutlicher.

13
Hans Malherbe

Ich habe mir Douglas Crockfords Video dazu angesehen und seine Erklärung dafür, dass er kein Inkrement und Dekrement verwendet, ist die folgende

  1. Es wurde in der Vergangenheit in anderen Sprachen verwendet, um die Grenzen von Arrays zu durchbrechen und alle Arten von Schlechtigkeit und Ungerechtigkeit hervorzurufen
  2. Dass es verwirrender und unerfahrener ist, wissen JS-Entwickler nicht genau, was es tut.

Erstens haben Arrays in JavaScript eine dynamische Größe. Wenn ich mich irre, ist es nicht möglich, die Grenzen eines Arrays zu überschreiten und auf Daten zuzugreifen, auf die mit dieser Methode in JavaScript nicht zugegriffen werden sollte.

Zweitens, sollten wir Dinge vermeiden, die kompliziert sind, besteht das Problem sicherlich nicht darin, dass wir diese Funktion haben, sondern darin, dass es Entwickler gibt, die behaupten, JavaScript zu machen, aber nicht wissen, wie diese Operatoren funktionieren? Es ist einfach genug. value ++, gib mir den aktuellen Wert und füge nach dem Ausdruck one hinzu, ++ value, erhöhe den Wert, bevor du ihn mir gibst.

Ausdrücke wie a ++ + ++ b sind einfach zu verstehen, wenn Sie sich nur an das Obige erinnern.

var a = 1, b = 1, c;
c = a ++ + ++ b;
// c = 1 + 2 = 3; 
// a = 2 (equals two after the expression is finished);
// b = 2;

Ich nehme an, Sie müssen sich nur merken, wer den Code lesen muss. Wenn Sie ein Team haben, das JS genau kennt, müssen Sie sich keine Sorgen machen. Wenn nicht, dann kommentiere es, schreibe es anders usw. Tu was du tun musst. Ich denke nicht, dass Inkrement und Dekrement von Natur aus schlecht oder fehlererzeugend oder verwundbar sind, möglicherweise nur weniger lesbar, abhängig von Ihrer Zielgruppe.

Übrigens denke ich, dass Douglas Crockford sowieso eine Legende ist, aber ich denke, dass er eine Menge Angst vor einem Betreiber verursacht hat, der es nicht verdient hat.

Ich lebe, um mich zu irren ...

13

Ein anderes Beispiel, einfacher als einige andere mit einfacher Rückgabe des inkrementierten Werts:

function testIncrement1(x) {
    return x++;
}

function testIncrement2(x) {
    return ++x;
}

function testIncrement3(x) {
    return x += 1;
}

console.log(testIncrement1(0)); // 0
console.log(testIncrement2(0)); // 1
console.log(testIncrement3(0)); // 1

Wie Sie sehen, sollte bei der return-Anweisung kein Inkrement/Dekrement verwendet werden, wenn dieser Operator das Ergebnis beeinflussen soll. Aber return "fängt" Operatoren nach dem Inkrementieren/Dekrementieren nicht:

function closureIncrementTest() {
    var x = 0;

    function postIncrementX() {
        return x++;
    }

    var y = postIncrementX();

    console.log(x); // 1
}
10
Evgeny Levin

Ich denke, Programmierer sollten in der Sprache, die sie verwenden, kompetent sein. benutze es klar; und benutze es gut. Ich glaube nicht , dass sie die Sprache, die sie benutzen, künstlich lähmen sollten. Ich spreche aus Erfahrung. Ich habe einmal buchstäblich neben einem Cobol-Laden gearbeitet, in dem sie ELSE nicht benutzten, "weil es zu kompliziert war". Reductio ad absurdam.

8
user207421

Meine 2 Cent sind, dass sie in zwei Fällen vermieden werden sollten:

1) Wenn Sie eine Variable haben, die in vielen Zeilen verwendet wird, und Sie sie bei der ersten Anweisung, die sie verwendet, erhöhen/verringern (oder zuletzt oder, noch schlimmer, in der Mitte):

// It's Java, but applies to Js too
vi = list.get ( ++i );
vi1 = list.get ( i + 1 )
out.println ( "Processing values: " + vi + ", " + vi1 )
if ( i < list.size () - 1 ) ...

In Beispielen wie diesem können Sie leicht übersehen, dass die Variable automatisch inkrementiert/dekrementiert wird, oder sogar die erste Anweisung entfernen. Mit anderen Worten, verwenden Sie es nur in sehr kurzen Blöcken oder dort, wo die Variable in dem Block nur bei ein paar Close-Anweisungen vorkommt.

2) Bei mehreren ++ und - über dieselbe Variable in derselben Anweisung. Es ist sehr schwer zu merken, was in solchen Fällen passiert:

result = ( ++x - --x ) * x++;

Prüfungen und professionelle Tests fragen nach Beispielen wie oben, und ich bin auf diese Frage gestoßen, als ich nach einer Dokumentation für eines von ihnen gesucht habe, aber im wirklichen Leben sollte man nicht gezwungen sein, so viel über eine einzige Codezeile nachzudenken.

2
zakmck

Nach meiner Erfahrung hat ++ i oder i ++ nie Verwirrung gestiftet, außer als ich zum ersten Mal erfuhr, wie der Bediener arbeitet. Es ist wichtig für die grundlegendsten for-Schleifen und while-Schleifen, die von jedem Highschool- oder College-Kurs unterrichtet werden, der in Sprachen unterrichtet wird, in denen Sie den Operator verwenden können. Ich persönlich finde es so etwas wie das, was unten steht, besser auszusehen und zu lesen als etwas, bei dem ein ++ in einer separaten Zeile steht.

while ( a < 10 ){
    array[a++] = val
}

Letztendlich handelt es sich um eine Stilvorliebe und nicht mehr. Wichtiger ist jedoch, dass Sie bei dieser Vorgehensweise in Ihrem Code konsistent bleiben, damit andere, die am selben Code arbeiten, folgen können und nicht dieselbe Funktionalität auf unterschiedliche Weise verarbeiten müssen .

Crockford scheint auch i- = 1 zu verwenden, was ich schwerer zu lesen finde als --i oder i--

2
burdz

Ich weiß nicht, ob dies Teil seiner Überlegungen war, aber wenn Sie ein schlecht geschriebenes Minimierungsprogramm verwenden, könnte es zu x++ + y into x+++y. Andererseits kann ein schlecht geschriebenes Werkzeug alle Arten von Chaos anrichten.

2
James M.

Wie in einigen der vorhandenen Antworten erwähnt (die ich leider nicht kommentieren kann), besteht das Problem darin, dass x ++ ++ x zu unterschiedlichen Werten ausgewertet wird (vor und nach dem Inkrement), was nicht offensichtlich ist und sehr verwirrend sein kann - if dieser Wert wird verwendet. cdmckay empfiehlt die Verwendung eines Inkrement-Operators mit Bedacht, jedoch nur so, dass der zurückgegebene Wert nicht verwendet wird, z. auf seiner eigenen Linie. Ich würde auch die Standardverwendung in eine for-Schleife aufnehmen (aber nur in die dritte Anweisung, deren Rückgabewert nicht verwendet wird). Ich kann mir kein anderes Beispiel vorstellen. Da ich mich selbst "verbrannt" habe, würde ich den gleichen Leitfaden auch für andere Sprachen empfehlen.

Ich bin mit der Behauptung nicht einverstanden, dass diese Überstrengung darauf zurückzuführen ist, dass viele JS-Programmierer unerfahren sind. Dies ist die exakte Art des Schreibens, die typisch für "übermäßig clevere" Programmierer ist, und ich bin sicher, dass dies in traditionelleren Sprachen und bei JS-Entwicklern, die über einen Hintergrund in solchen Sprachen verfügen, weitaus häufiger vorkommt.

2
Near Privman

Ist Fortran eine C-ähnliche Sprache? Es hat weder ++ noch -. Hier ist wie Sie eine Schleife schreiben :

     integer i, n, sum

      sum = 0
      do 10 i = 1, n
         sum = sum + i
         write(*,*) 'i =', i
         write(*,*) 'sum =', sum
  10  continue

Das Indexelement i wird jedes Mal durch die Schleife um die Sprachregeln erhöht. Wenn Sie um etwas anderes als 1 inkrementieren möchten, beispielsweise um zwei rückwärts zählen möchten, lautet die Syntax ...

      integer i

      do 20 i = 10, 1, -2
         write(*,*) 'i =', i
  20  continue

Ist Python C-like? Verwendet Bereichs- und Listenverständnisse und andere Syntaxen, um das Erhöhen eines Index zu umgehen:

print range(10,1,-2) # prints [10,8.6.4.2]
[x*x for x in range(1,10)] # returns [1,4,9,16 ... ]

Basierend auf dieser rudimentären Untersuchung von genau zwei Alternativen können Sprachdesigner ++ und - vermeiden, indem sie Anwendungsfälle antizipieren und eine alternative Syntax bereitstellen.

Sind Fortran und Python deutlich weniger ein Bugmagnet als prozedurale Sprachen mit ++ und -? Ich habe keine Beweise.

Ich behaupte, dass Fortran und Python C-like sind, weil ich noch nie jemanden getroffen habe, der C fließend beherrscht und nicht mit einer Genauigkeit von 90% die Absicht von nicht verschleiertem Fortran oder Python richtig erraten konnte.

1