Cosa fa “! -” in JavaScript?


376

Ho questo pezzo di codice (tratto da questa domanda ):

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

Sto provando a seguirlo, e penso di capire tutto tranne che verso la fine dove dice !--pending. In questo contesto, cosa fa quel comando?

Modifica: apprezzo tutti gli ulteriori commenti, ma alla domanda è stata data risposta molte volte. Grazie comunque!


222
È un modo meraviglioso di confondere la persona successiva per mantenere il codice.
Eric J.

240
!~--[value] ^ trueChiamo codice come questo "sicurezza del lavoro"
TbWill4321


36
@ TbWill4321 Se stavo facendo la revisione del codice, sarebbe esattamente l'opposto della sicurezza del lavoro.
corsiKa

8
Gli operatori combinati con il nome della variabile rende non è poi così male. Qualsiasi programmatore Javascript esperto non avrà bisogno di più di un paio di secondi per sapere cosa fa.
Christiaan Westerbeek

Risposte:


537

! inverte un valore e ti dà il valore booleano opposto:

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

--[value] sottrae uno (1) da un numero, quindi restituisce quel numero con cui lavorare:

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

Così, !--pending sottrae una persona in sospeso, quindi restituisce l'opposto del suo valore di verità / falsità (indipendentemente dal fatto che sia 0).

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

E sì, segui il suggerimento. Questo può essere un linguaggio comune in altri linguaggi di programmazione, ma per la maggior parte della programmazione JavaScript dichiarativa sembra abbastanza alieno.


623
ProTip ™: non farlo mai nel tuo codice, è ridicolo.
Naftuli Kay,

17
Questo non menziona che --agisce solo sulle variabili; non puoi applicarlo ai valori in generale.
Delta del

10
@deltab: validSimpleAssignmentTargets, per essere esatti. Ciò include riferimenti identificativi e riferimenti di proprietà.
Bergi,

19
--0e --1non funzionerà. I letterali numerici non sono validi espressione a sinistra
PSWai

7
@Pharap: Uh, ho più problemi ad analizzare quella i > -1cosa di i--(e tra l'altro te ne sei dimenticato i = init() - 1). Ecco a cosa servono gli idiomi ... e ogni programmatore dovrebbe impararli.
Bergi,

149

Non è un operatore speciale, sono 2 operatori standard uno dopo l'altro:

  1. Un decremento prefisso (-- )
  2. Un non logico ( !)

Questo provoca pendingun decremento e quindi il test per vedere se è zero.


8
Sono sicuramente nuovo a questo, ma perché è superiore all'utilizzo if(pending === 1)?
Kieran E

46
@KieranE, non lo è, è una scorciatoia che infrange completamente la regola della "leggibilità è re".
Ryan,

23
@KieranE non si tratta di essere superiore, è diverso. Muta la variabile (la diminuisce di 1) e quindi usa il nuovo valore per testare
Amit

7
@KieranE, è equivalente a if( pending === 1 ) { done... } else { pending = pending - 1; }.
CompuChip il

7
@CompuChip In realtà sembra che - in sospeso verrebbe fatto in ogni caso, quindi il codice è più simile a: if (! Pending) {--pending; ecc.} else {--pending; ecc.} Questo si basa su developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Paul Russell,

109

Un numero di risposte descrive cosa fa questo comando, ma non perché qui viene fatto in questo modo.

Vengo dal mondo C e leggo !--pendingcome "conto alla rovesciapending e controlla se è zero" senza pensarci davvero. È un linguaggio che penso che i programmatori in lingue simili dovrebbero conoscere.

La funzione utilizza readdir per ottenere un elenco di file e sottodirectory, che chiamerò collettivamente "voci".

La variabile pendingtiene traccia di quanti di questi rimangono da elaborare. Inizia come la lunghezza dell'elenco e conta verso il basso verso zero man mano che ogni voce viene elaborata.

Queste voci possono essere elaborate in modo anomalo, motivo per cui è necessario eseguire il conto alla rovescia anziché semplicemente utilizzare un semplice ciclo. Quando tutte le voci sono state elaborate, il callback doneviene chiamato per avvisare il chiamante originale di questo fatto.

Nella prima chiamata a doneviene anteposto return, non perché vogliamo restituire un valore, ma semplicemente per far cessare l'esecuzione della funzione in quel punto. Sarebbe stato un codice più pulito eliminare returne inserire l'alternativa in un else.


13
@Bergi si tratta di un linguaggio ben noto nel mondo C, ma è non è idiomatica Javascript. Per ogni data sottoespressione, diversi ecosistemi avranno una varietà di modi di dire diversi e incompatibili, come "sia fatto" lì. Usare idiomi da lingue "straniere" è un odore di codice piuttosto cattivo.
Peteris,

3
@Peteris: è un linguaggio in tutti i linguaggi di tipo C che hanno l'operatore di decremento. Certo, a volte ci sono modi diversi e migliori disponibili in una lingua, ma non credo che JS abbia dei modi di dire speciali.
Bergi,

6
V'è una parte significativa della comunità Javascript, tra cui i leader influenti come Douglas Crockford, che l'avvocato evitando gli operatori di incremento e decremento unario del tutto . Non discuterò qui se sono corretti o no; il mio intento è solo quello di sottolineare che, poiché esiste la controversia, il codice come !--pendingfallirà molte linters e linee guida di stile. Questo mi basta per dire che probabilmente non è idiomatico (indipendentemente dal fatto che l'alternativa sia "migliore").
GrandOpener,

3
@GrandOpener "Idiomatico" significa "un'espressione comunemente compresa dai madrelingua". Predecrement è sicuramente ben compreso dagli utenti di linguaggi simili a C e, per estensione, lo è anche "! (- x)". Se usare qualcosa sia una buona idea® è una domanda completamente separata da quanto sia idiomatica. (Ad esempio, dubito fortemente che incontrerai molti programmatori di qualsiasi lingua che non capiscono cosa fa "goto", nonostante l'opinione espressa di frequente che includerlo nel tuo codice rientri tra "Lust" e "Murder" nell'elenco degli otto peccati
capitali

3
@Pharap Questo perché C # e Java non convertire intal boolimplicitamente, e non ha assolutamente nulla a che fare con l'utilizzo di !--.
Agop,

36

È una scorciatoia.

! non è".

-- decrementa un valore.

Quindi !--controlla se il valore ottenuto negando il risultato del decremento di un valore è falso.

Prova questo:

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

Il primo è falso, poiché il valore di x è 1, il secondo è vero, poiché il valore di x è 0.

Nota a margine: !x--verificherebbe prima se x è falso, quindi lo decrementerà.


RE la nota a margine - sei sicuro? Sembra che post-fix abbia una precedenza maggiore rispetto a !, mentre pre-fix ha una precedenza inferiore. Precedenza dell'operatore JavaScript
Dannnno,

2
Sì. (Prova var x = 2; console.log(!x--); console.log(!x--); console.log(!x--);). Mentre The post-fix --potrebbe essere eseguito per primo, il suo valore di ritorno è il valore della variabile prima di diminuire ( Decrement Opperator ).
Lucas,

Penso che intendevi dire " !--restituisce la negazione del risultato del decremento di un valore". Solo un codice esterno, ad esempio console.log(), controlla se il suo contenuto è veritiero.
mareoraft,

31

!è l' operatore JavaScript NOT

--è un operatore pre-decremento. Così,

x = 1;
if (!x) // false
if (!--x) // becomes 0 and then uses the NOT operator,
          // which makes the condition to be true

8
WTH è una "dichiarazione falsa"?
Bergi il

5
@Bergi Suppongo che tu effettivamente sappia cosa significa, ma nel caso in cui tu non lo faccia (o chiunque altro non lo fa) ecco una spiegazione per JavaScript che si traduce anche in qualche modo bene in altre lingue con questo concetto (come Python).
Dannnno,

1
@Pharap non è la mia risposta, quindi non la toccherò.
Dannnno,

2
@Dannnno: Beh, so quali sono i valori falsi e quali sono le dichiarazioni , e quindi so che non esiste una cosa come una "dichiarazione falsa" in JS. Presumo che questo non si riferisca al termine logico .
Bergi,

1
Non capisco come l' --operatore possa lavorare con una costante ... forse intendevi --xinvece di --0?
SJuan76,

24
if(!--pending)

si intende

if(0 == --pending)

si intende

pending = pending - 1;
if(0 == pending)

4
E questo è il modo chiaro per scrivere il codice. Certo, preferisco a if(0 == pending)causa del potenziale per errori di battitura. if(0 = pending)è un errore di sintassi. if(pending = 0)è un compito che causerà comportamenti confusi nel codice finito.
Theo Brinkman,

3
@TheoBrinkman: in realtà if(0 = pending)non è un errore di sintassi - analizza bene - ma un errore di riferimento perché 0non è assegnabile (rif ECMA-262 (sesto ed) sec 12.14.1).
Hmakholm ha lasciato Monica il

13

È l'operatore non seguito dal pre-decrementatore sul posto.

Quindi se pendingfosse un numero intero con un valore di 1:

val = 1;
--val; // val is 0 here
!val // evaluates to true

11

Diminuisce semplicemente pendingdi uno e ottiene il suo complemento logico (negazione). Il complemento logico di qualsiasi numero diverso da 0 è false, per 0 lo è true.


Si noti che "negare" ha un significato diverso sui numeri rispetto ai valori booleani
Bergi,

1
Bene, userò un termine diverso. Non sono sicuro che sia meglio però.
Meno quattro il

11

Spiegazione

Sono 2 operatori, a !e a--

!--x 

Quindi, --decrementa x di 1, quindi !restituisce true se x è ora 0 (o NaN ...), false se non lo è. Potresti leggere questo idioma qualcosa del tipo "decrementiamo x e se questo lo rende zero ..."

Se vuoi renderlo più leggibile, puoi:

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

Provalo:

/* 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>

Fiddle (codice di prova)


8

Il vero problema qui è la mancanza di uno spazio tra i due operatori !e --.

Non so perché la gente si renda conto che non puoi mai usare uno spazio dopo l' !operatore. Penso che provenga dall'applicazione rigida delle regole meccaniche degli spazi bianchi invece del buon senso. Quasi tutti gli standard di codifica che ho visto vietano gli spazi dopo tutti gli operatori unari, ma perché?

Se ci fosse mai un caso in cui hai chiaramente bisogno di quello spazio, questo è uno.

Considera questo bit di codice:

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

Non solo vengono !e --schiacciati insieme, ma li hai anche (colpiti. Non c'è da stupirsi che sia difficile dire cosa sia connesso a cosa.

Un po 'più di spazio bianco rende il codice molto più chiaro:

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

Certo, se sei abituato a regole meccaniche come "nessuno spazio dentro le parentesi" e "nessuno spazio dopo un operatore unario", questo può sembrare un po 'estraneo.

Ma guarda come lo spazio extra in bianco raggruppa e separa le varie parti ifdell'affermazione e dell'espressione: hai --pending, quindi --è chiaramente il suo operatore ed è strettamente legato pending. (Diminuisce pendinge restituisce il risultato decrementato.) Quindi hai !separato da quello quindi è ovviamente un operatore distinto, annullando il risultato. Alla fine, hai if(e )circostante l'intera espressione per renderla una ifdichiarazione.

E sì, ho rimosso lo spazio tra ife (, poiché ( appartiene al if. Questo (non fa parte di alcun tipo di (!--sintassi come sembra essere nell'originale, la (parte if della sintassi ifdell'istruzione stessa.

Lo spazio bianco qui serve a comunicare il significato , invece di seguire alcuni standard di codifica meccanica.


1
La versione bianca è più leggibile per me. Quando ho visto la domanda per la prima volta ho pensato che !--fosse un operatore javascript che non conoscevo. Perché non usare le parentesi per renderlo ancora più esplicito? if(!(--pending))
emory

1
Non ci sono guide di stile sono consapevole di vietare utilizzando ++o --, ma molti ne vietano l'utilizzo spazi non essenziali, come dopo l' !operatore di prefisso, e parentesi non essenziali.

1
Lo spazio bianco dopo gli operatori unari è scoraggiato perché lo separa dall'espressione su cui opera. Vorresti che fosse letto insieme ... almeno imho
aldrin il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.