Was macht "! -" in JavaScript?

376

Ich habe diesen Code (aus dieser Frage entnommen ):

var walk = function(dir, done) {
    var results = [];

    fs.readdir(dir, function(err, list) {
        if (err)
            return done(err);

        var pending = list.length;

        if (!pending) 
            return done(null, results);

        list.forEach(function(file) {
            file = path.resolve(dir, file);
            fs.stat(file, function(err, stat) {
                if (stat && stat.isDirectory()) {
                    walk(file, function(err, res) {
                        results = results.concat(res);

                        if (!--pending)
                            done(null, results);
                    });
                } else {
                    results.push(file);

                    if (!--pending) 
                        done(null, results);
                }
            });
        });
    });
};

Ich versuche, ihm zu folgen, und ich glaube, ich verstehe alles, außer gegen Ende, wo es heißt !--pending. Was macht dieser Befehl in diesem Zusammenhang?

Edit: Ich freue mich über alle weiteren Kommentare, aber die Frage wurde schon oft beantwortet. Danke trotzdem!

Kieran E.
quelle
222
Es ist eine wunderbare Möglichkeit, die nächste Person zu verwirren, die den Code verwaltet.
Eric J.
240
!~--[value] ^ trueIch nenne Code wie diesen "Job Security"
TbWill4321
63
Das erinnert mich an Wie heißt der -->Betreiber?
Soner Gönül
36
@ TbWill4321 Wenn ich die Codeüberprüfung durchführen würde, wäre dies genau das Gegenteil von Arbeitsplatzsicherheit.
CorsiKa
8
Die Operatoren in Kombination mit dem Variablennamen make sind nicht schlecht. Jeder erfahrene Javascript-Programmierer benötigt nicht mehr als ein paar Sekunden, um zu wissen, was er tut.
Christiaan Westerbeek

Antworten:

537

! Invertiert einen Wert und gibt Ihnen den entgegengesetzten Booleschen Wert:

!true == false
!false == true
!1 == false
!0 == true

--[value] subtrahiert eins (1) von einer Zahl und gibt dann die Zahl zurück, mit der gearbeitet werden soll:

var a = 1, b = 2;
--a == 0
--b == 1

Also, !--pendingsubtrahiert man aus schwebenden und kehrt dann das Gegenteil von seiner truthy / falsy Wert (ob oder nicht es ist 0).

pending = 2; !--pending == false 
pending = 1; !--pending == true
pending = 0; !--pending == false

Und ja, folgen Sie dem ProTip. Dies mag in anderen Programmiersprachen eine gängige Redewendung sein, aber für die meisten deklarativen JavaScript-Programme sieht dies ziemlich fremd aus.

TbWill4321
quelle
623
ProTip ™: Tun Sie dies niemals in Ihrem Code, es ist lächerlich.
Naftuli Kay
17
Dies erwähnt nicht, dass --nur auf Variablen wirkt; Sie können es im Allgemeinen nicht auf Werte anwenden.
Deltab
10
@deltab: validSimpleAssignmentTargets, um genau zu sein. Dies umfasst Bezeichnerreferenzen und Eigenschaftsreferenzen.
Bergi
19
--0und --1wird nicht funktionieren. Numerische Literale sind kein gültiger Ausdruck auf der linken Seite
PSWai
7
@Pharap: Äh, ich habe mehr Probleme beim Parsen i > -1als i--(und übrigens hast du es vergessen i = init() - 1). Dafür sind Redewendungen da ... und jeder Programmierer sollte sie lernen.
Bergi
149

Das ist kein spezieller Operator, es sind 2 Standardoperatoren nacheinander:

  1. Ein Präfixdekrement ( --)
  2. Ein logisches nicht ( !)

Dies führt pendingdazu, dass dekrementiert und dann getestet wird , um festzustellen, ob es Null ist.

Amit
quelle
8
Ich bin definitiv neu in diesem Bereich, aber warum ist dies der Verwendung überlegen if(pending === 1)?
Kieran E
46
@KieranE, es ist nicht, es ist eine Abkürzung, die die Regel "Lesbarkeit ist König" vollständig bricht.
Ryan
23
@KieranE Es geht nicht darum, überlegen zu sein, es ist anders. Es mutiert die Variable (verringert sie um 1) und verwendet dann den neuen Wert zum Testen
Amit
7
@KieranE, es ist äquivalent zu if( pending === 1 ) { done... } else { pending = pending - 1; }.
CompuChip
7
@CompuChip Es sieht tatsächlich so aus, als würde --pending auf jeden Fall durchgeführt, daher lautet der Code eher wie folgt: if (! Pending) {--pending; usw.} else {--pending; usw.} Dies basiert auf developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Paul Russell
109

Eine Reihe von Antworten beschreibt, was dieser Befehl tut, aber nicht, warum er hier so ausgeführt wird.

Ich komme aus der C-Welt und lese !--pendingals "Countdown pendingund überprüfe, ob es Null ist", ohne wirklich darüber nachzudenken. Es ist eine Redewendung, die Programmierer in ähnlichen Sprachen meiner Meinung nach kennen sollten.

Die Funktion verwendet readdir, um eine Liste von Dateien und Unterverzeichnissen zu erhalten, die ich zusammen "Einträge" nennen werde.

Die Variable pendingverfolgt, wie viele davon noch verarbeitet werden müssen. Es beginnt als Länge der Liste und zählt bei jeder Verarbeitung nach unten in Richtung Null.

Diese Einträge können außerhalb der Reihenfolge verarbeitet werden, weshalb ein Countdown erforderlich ist, anstatt nur eine einfache Schleife zu verwenden. Wenn alle Einträge verarbeitet wurden, wird der Rückruf doneaufgerufen, um den ursprünglichen Anrufer über diese Tatsache zu informieren.

Im ersten Aufruf von donewird nicht vorangestellt return, nicht weil wir einen Wert zurückgeben möchten, sondern einfach, damit die Funktion an diesem Punkt nicht mehr ausgeführt wird. Es wäre sauberer gewesen, den Code fallen zu lassen returnund die Alternative in einen zu setzen else.

Stig Hemmer
quelle
13
@Bergi es ist eine bekannte Redewendung in der C-Welt, aber es ist kein idiomatisches Javascript. Für jeden Subexpression haben verschiedene Ökosysteme verschiedene, inkompatible Redewendungen, wie es dort gemacht wird. Die Verwendung von Redewendungen aus "Fremdsprachen" ist ein ziemlich schlechter Codegeruch.
Peteris
3
@Peteris: Es ist eine Redewendung in allen C-ähnlichen Sprachen, die den Dekrementoperator haben. Natürlich gibt es manchmal verschiedene, bessere Möglichkeiten in einer Sprache, aber ich glaube nicht, dass JS spezielle Zählsprachen hat.
Bergi
6
Es gibt einen bedeutenden Teil der Javascript-Community, einschließlich einflussreicher Führungskräfte wie Douglas Crockford, die sich dafür einsetzen, die unären Inkrementierungs- und Dekrementierungsoperatoren vollständig zu vermeiden . Ich werde hier nicht darüber streiten, ob sie richtig sind oder nicht; Ich möchte nur darauf hinweisen, dass Code wie !--pendingviele Linters und Stilrichtlinien fehlschlagen wird, da die Kontroverse besteht . Das reicht mir zu sagen, dass es wahrscheinlich nicht idiomatisch ist (unabhängig davon, ob die Alternative "besser" ist).
GrandOpener
3
@GrandOpener "Idiomatisch" bedeutet "ein Ausdruck, der von Muttersprachlern allgemein verstanden wird". Die Voreinstellung ist für Benutzer von C-ähnlichen Sprachen definitiv gut verstanden, und im weiteren Sinne auch "! (- x)". Ob die Verwendung von etwas eine gute Idee ist oder nicht , ist eine völlig andere Frage als die Redewendung. (ZB bezweifle ich sehr, dass Sie auf viele Programmierer jeder Sprache stoßen, die nicht verstehen, was "goto" tut, trotz der häufig geäußerten Meinung, dass die Aufnahme in Ihren Code irgendwo zwischen "Lust" und "Murder" auf der Liste liegt der acht Todsünden.)
jmbpiano
3
@Pharap Das liegt daran , C # und Java nicht konvertieren intzu boolimplizit, und hat absolut nichts mit der Nutzung zu tun !--.
Agop
36

Es ist eine Abkürzung.

! ist nicht".

-- dekrementiert einen Wert.

!--Überprüft also , ob der Wert, der durch Negieren des Ergebnisses der Dekrementierung eines Werts erhalten wird, falsch ist.

Versuche dies:

var x = 2;
console.log(!--x);
console.log(!--x);

Der erste ist falsch, da der Wert von x 1 ist, der zweite ist wahr, da der Wert von x 0 ist.

Randnotiz: !x--würde zuerst prüfen, ob x falsch ist, und es dann dekrementieren.

Lucas
quelle
RE die Randnotiz - sind Sie sicher? Es sieht so aus, als hätte Post-Fix eine höhere Priorität als !Pre-Fix, während Pre-Fix eine niedrigere Priorität hat. JavaScript Operator Vorrang
Dannnno
2
Ja. (Versuchen Sie es var x = 2; console.log(!x--); console.log(!x--); console.log(!x--);). Während das Post-Fix --möglicherweise zuerst ausgeführt wird, ist sein Rückgabewert der Wert der Variablen vor dem Dekrementieren ( Decrement Opperator ).
Lucas
Ich denke, Sie wollten sagen: " !--Gibt die Negation des Ergebnisses der Dekrementierung eines Werts zurück." Nur console.log()externer Code, z. B. , prüft, ob sein Inhalt wahr ist.
Mareoraft
31

!ist der JavaScript NOT- Operator

--ist ein Operator vor dem Dekrementieren. Damit,

x = 1;
if (!x) // false
if (!--x) // becomes 0 and then uses the NOT operator,
          // which makes the condition to be true
Sterling Archer
quelle
8
WTH ist eine "falsche Aussage"?
Bergi
5
@Bergi Ich gehe davon aus, dass Sie tatsächlich wissen, was es bedeutet, aber falls Sie dies nicht tun (oder jemand anderes nicht), finden Sie hier eine Erklärung für JavaScript , die mit diesem Konzept auch in andere Sprachen (wie Python) übersetzt werden kann.
Dannnno
1
@Pharap es ist nicht meine Antwort, also werde ich es nicht anfassen.
Dannnno
2
@Dannnno: Nun, ich weiß, was falsche Werte sind und welche Aussagen , und deshalb weiß ich, dass es in JS keine "falsche Aussage" gibt. Ich gehe davon aus, dass sich dies nicht auf den logischen Begriff bezieht .
Bergi
1
Ich verstehe nicht, wie der --Operator mit einer Konstanten arbeiten könnte ... vielleicht meinten Sie --xstattdessen --0?
SJuan76
24
if(!--pending)

meint

if(0 == --pending)

meint

pending = pending - 1;
if(0 == pending)
James Turner
quelle
4
Und das ist der klare Weg, um den Code zu schreiben. Natürlich bevorzuge ich if(0 == pending)wegen des Potenzials für Tippfehler. if(0 = pending)ist ein Syntaxfehler. if(pending = 0)ist eine Aufgabe, die im fertigen Code zu verwirrendem Verhalten führt.
Theo Brinkman
3
@TheoBrinkman: ist eigentlich if(0 = pending)kein Syntaxfehler - es wird gut analysiert - sondern ein Referenzfehler, da er 0nicht zuweisbar ist (siehe ECMA-262 (6. Ausgabe), Abschnitt 12.14.1).
Hmakholm verließ Monica
13

Es ist der Nicht-Operator, gefolgt vom In-Place-Vordekrementierer.

Wenn also pendingeine Ganzzahl mit dem Wert 1 wäre:

val = 1;
--val; // val is 0 here
!val // evaluates to true
Brendan Abel
quelle
11

Es nimmt lediglich um pendingeins ab und erhält seine logische Ergänzung (Negation). Das logische Komplement einer beliebigen Zahl false, die sich von 0 unterscheidet, ist für 0 true.

MinusFour
quelle
Bitte beachten Sie, dass "negieren" auf Zahlen eine andere Bedeutung hat als auf Booleschen Werten
Bergi
1
Okay, ich werde einen anderen Begriff verwenden. Ich bin mir nicht sicher, ob das besser ist.
MinusFour
11

Erläuterung

Dies sind 2 Operatoren, a !und a--

!--x 

Also --dekrementiert x um 1, dann !gibt true zurück, wenn x jetzt 0 ist (oder NaN ...), false, wenn dies nicht der Fall ist. Sie könnten diese Redewendung so etwas wie "Wir dekrementieren x und wenn das Null macht ..." lesen.

Wenn Sie es lesbarer machen möchten, können Sie:

var x = 1
x = x - 1   
if(!x){ //=> true
    console.log("I understand `!--` now!") 
}
x //=> 0

Versuch es:

/* This is an example of the above, you can read this, but it is not needed for !-- */function interactive(a){$("span.code").keydown(function(e){if(13==(e.keyCode||e.which)){var t=$(this);t.clone().html("code").insertAfter(t.next().next()).show().focus().after(template.clone().removeClass("result-template").show()).next().after("<br>"),interactive(),e.preventDefault()}}).keyup(function(e){13!=(e.keyCode||e.which)&&run()})}var template=$(".result-template").hide(),code=$("span.code");code.attr("contenteditable","true").each(function(e,t){template.clone().removeClass("result-template").insertAfter(t)}),interactive(),$.fn.reduce=[].reduce;function run(){var b=!1,context={};$("span.code").each(function(){var a=$(this),res=a.next().show().removeClass("error");try{with(context)res.html(b?"":"  //=> "+eval(a.text()))}catch(e){b=e,res.html("  Error: "+b.message).addClass("error")}})};run();
/* This is an example of the above, you can read this, but it is not needed for !-- */span.result.error{display:block;color:red}.code{min-width:10px}body{font-family:Helvetica,sans-serif}
<!-- This is an example of the above, you can read this, but it is not needed for `!--` --><span class="result result-template"> //=> unknown </span> <h2>Edit This Code:</h2><code><span class="code">x = 1</span><br><span class="code">!--x</span><br><span class="code"> x </span><br></code> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Geige (Code ausprobieren)

Ben Aubin
quelle
8

Das eigentliche Problem hierbei ist das Fehlen eines Leerzeichens zwischen den beiden Operatoren !und --.

Ich weiß nicht, warum die Leute es in ihren Köpfen verstehen, dass man nach dem !Operator niemals ein Leerzeichen verwenden kann . Ich denke, es kommt von der starren Anwendung mechanischer Leerzeichenregeln anstelle des gesunden Menschenverstandes. Fast jeder Codierungsstandard, den ich gesehen habe, verbietet Leerzeichen nach allen unären Operatoren, aber warum?

Wenn es jemals einen Fall gab, in dem Sie diesen Platz eindeutig benötigen , ist dies einer.

Betrachten Sie diesen Code:

if (!--pending)
    done(null, results);

Nicht nur !und-- püriert, du hast das auch (gegen sie geschlagen. Kein Wunder, dass es schwer zu sagen ist, was mit was verbunden ist.

Ein bisschen mehr Leerzeichen macht den Code viel klarer:

if( ! --pending )
    done( null, results );

Sicher, wenn Sie an mechanische Regeln wie "kein Platz in Parens" und "kein Platz nach einem unären Operator" gewöhnt sind, scheint dies etwas fremd zu sein.

Aber schauen Sie sich an, wie das zusätzliche Leerzeichen die verschiedenen Teile der ifAnweisung und des Ausdrucks gruppiert und trennt : Sie haben --pending, also --ist das eindeutig sein eigener Operator und eng mit ihm verbunden pending. (Es dekrementiert pendingund gibt das dekrementierte Ergebnis zurück.) Dann haben Sie das davon !getrennt, so dass es offensichtlich ein eindeutiger Operator ist, der das Ergebnis negiert. Schließlich haben Sie , if(und )den gesamten Ausdruck umgibt , um es eine machen ifAussage.

Und ja, entfernte ich den Raum zwischen ifund (, weil das ( gehört zu dem if. Dies (ist nicht Teil einer (!--Syntax, wie es im Original zu sein scheint, der (if-Teil der Syntax der ifAnweisung selbst.

Das Leerzeichen dient hier dazu, die Bedeutung zu kommunizieren , anstatt einem mechanischen Codierungsstandard zu folgen.

Michael Geary
quelle
1
Die Whitespaced-Version ist für mich besser lesbar. Als ich die Frage zum ersten Mal sah, nahm ich an, dass !--es sich um einen Javascript-Operator handelte, den ich nicht kannte. Warum nicht Klammern verwenden, um es noch deutlicher zu machen? if(!(--pending))
Emory
1
Keine Styleguides ich weiß verbieten verwenden ++oder --, aber viele tun verbieten unter Verwendung von nicht-essentielle Räume wie nach dem !Präfix - Operator, und nicht wesentliche Klammern.
1
Leerzeichen nach unären Operatoren werden nicht empfohlen, da sie von dem Ausdruck, mit dem sie arbeiten, getrennt werden. Sie würden wollen , dass es zusammen ... zumindest imho gelesen werden
Aldrin