O que "! -" faz em JavaScript?

376

Eu tenho esse pedaço de código (retirado desta pergunta ):

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

Estou tentando segui-lo e acho que entendo tudo, exceto no final, onde diz !--pending . Nesse contexto, o que esse comando faz?

Edit: Agradeço todos os comentários, mas a pergunta foi respondida muitas vezes. Obrigado mesmo assim!

Kieran E
fonte
222
É uma maneira maravilhosa de confundir a próxima pessoa para manter o código.
Eric J.
240
!~--[value] ^ trueEu código de chamada como esta "segurança no emprego"
TbWill4321
63
Isso me lembra Qual é o nome do -->operador?
Soner Gönül
36
@ TbWill4321 Se eu estivesse revisando o código, seria exatamente o oposto da segurança no trabalho.
corsiKa
8
Os operadores combinados com o nome da variável make não são tão ruins. Qualquer programador Javascript experiente não precisará de mais de alguns segundos para saber o que faz.
Christiaan Westerbeek

Respostas:

537

! inverte um valor e fornece o booleano oposto:

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

--[value] subtrai um (1) de um número e, em seguida, retorna esse número a ser trabalhado:

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

Portanto, !--pendingsubtrai um de pendente e retorna o oposto de seu valor de verdade / falsidade (seja ou não 0).

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

E sim, siga a ProTip. Esse pode ser um idioma comum em outras linguagens de programação, mas para a maior parte da programação declarativa de JavaScript isso parece bastante estranho.

TbWill4321
fonte
623
ProTip ™: nunca faça isso no seu código, é ridículo.
Naftuli Kay
17
Isso não menciona que --atua apenas em variáveis; você não pode aplicá-lo aos valores em geral.
Deltab
10
@deltab: validSimpleAssignmentTargets, para ser exato. Isso inclui referências de identificador e referências de propriedade.
Bergi
19
--0e --1não vai funcionar. Literais numéricos não são válidos expressão do lado esquerdo
PSWai 17/15/15
7
@ Sharap: Uh, eu tenho mais problemas para analisar essa i > -1coisa do que i--(e você esqueceu i = init() - 1). É para isso que servem os idiomas ... e todo programador deve aprendê-los.
21915 Bergi
149

Não é um operador especial, são dois operadores padrão, um após o outro:

  1. Um decremento de prefixo ( --)
  2. Um não lógico ( !)

Isso faz pendingcom que seja diminuído e testado para ver se é zero.

Amit
fonte
8
Eu sou definitivamente novo nisso, mas por que isso é superior ao uso if(pending === 1)?
Kieran E
46
@KieranE, não é, é a abreviação que quebra completamente a regra de "legibilidade é rei".
22915 Ryan
23
@KieranE não se trata de ser superior, é diferente. Ele modifica a variável (diminui em 1) e, em seguida, usa o novo valor para testar
Amit
7
@KieranE, é equivalente a if( pending === 1 ) { done... } else { pending = pending - 1; }.
CompuChip
7
@CompuChip Na verdade, parece que - a suspensão seria feita em qualquer caso, portanto o código é mais como: if (! Pendending) {--pending; etc} else {--pending; etc.} Isso é baseado em developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/…
Paul Russell
109

Várias respostas descrevem o que esse comando faz, mas não o porquê de ser feito dessa maneira aqui.

Eu venho do mundo C e leio !--pendingcomo "faça uma contagem regressiva pendinge verifique se é zero" sem realmente pensar nisso. É um idioma que eu acho que os programadores em linguagens semelhantes deveriam conhecer.

A função usa readdir para obter uma lista de arquivos e subdiretórios, que eu chamarei coletivamente de "entradas".

A variável pendingmonitora quantos deles ainda precisam ser processados. Começa como o comprimento da lista e conta para baixo em direção a zero à medida que cada entrada é processada.

Essas entradas podem ser processadas fora de ordem, e é por isso que é necessário fazer uma contagem regressiva em vez de apenas usar um loop simples. Quando todas as entradas foram processadas, o retorno de chamada doneé chamado para notificar o chamador original desse fato.

Na primeira chamada, o to doneé anexado return, não porque queremos retornar um valor, mas simplesmente para fazer com que a função pare de ser executada nesse ponto. Teria sido um código mais limpo descartar o returne colocar a alternativa em um else.

Stig Hemmer
fonte
13
@Bergi é uma expressão bem conhecida no C mundo, mas é não idiomática Javascript. Para qualquer subexpressão, ecossistemas diferentes terão uma variedade de expressões diferentes e incompatíveis, como 'é feito' lá. Usar idiomas de idiomas 'estrangeiros' é um cheiro de código bastante ruim.
Peteris
3
@ Peteris: É um idioma em todos os idiomas do tipo C que possuem o operador de decremento. É claro que, às vezes, existem maneiras diferentes e melhores disponíveis em um idioma, mas não acho que o JS tenha idiomas de contagem especiais.
Bergi
6
Há uma parte significativa da comunidade Javascript, incluindo líderes influentes como Douglas Crockford, que defendem evitar os operadores de incremento e decréscimo unários por completo . Não vou discutir aqui se estão corretas ou não; minha intenção é apenas ressaltar que, como a controvérsia existe, o código like !--pendingfalhará em muitos linters e diretrizes de estilo. É o suficiente para eu dizer que provavelmente não é idiomático (independentemente de a alternativa ser "melhor").
GrandOpener
3
@GrandOpener "Idiomatic" significa "uma expressão comumente entendida por falantes nativos". A predileção é definitivamente bem compreendida pelos usuários de linguagens do tipo C e, por extensão, também é "! (- x)". Se usar ou não algo é uma Boa Idéia® é uma questão totalmente separada de quão idiomática é. (Por exemplo, duvido muito que você encontre muitos programadores de qualquer idioma que não entendam o que "goto" faz, apesar da opinião frequentemente expressa de que incluí-lo em seu código está entre "Lust" e "Murder" na lista dos oito pecados capitais.)
jmbpiano
3
@Pharap Isso porque C # e Java não converter intpara boolimplicitamente, e não tem absolutamente nada a ver com o uso de !--.
Agop 23/12/2015
36

É uma taquigrafia.

! não é".

-- diminui um valor.

Portanto, !--verifica se o valor obtido ao negar o resultado da diminuição de um valor é falso.

Tente o seguinte:

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

O primeiro é falso, já que o valor de x é 1, o segundo é verdadeiro, pois o valor de x é 0.

Nota lateral: !x--verificaria primeiro se x é falso e depois o diminuiria.

Lucas
fonte
RE a nota lateral - você tem certeza? Parece que a pós-correção tem precedência mais alta que !, enquanto a pré-correção tem precedência mais baixa. Precedência do operador JavaScript
Dannnno
2
Sim. (Tente var x = 2; console.log(!x--); console.log(!x--); console.log(!x--);). Embora o pós-reparo --possa ser executado primeiro, seu valor de retorno é o valor da variável antes de decrementar ( Decrement Opperator ).
Lucas
Eu acho que você quis dizer " !--retorna a negação do resultado da diminuição de um valor". Somente código externo, como console.log(), verifica se seu conteúdo é verdadeiro.
mareoraft
31

!é o operador JavaScript NOT

--é um operador de pré-decremento. Assim,

x = 1;
if (!x) // false
if (!--x) // becomes 0 and then uses the NOT operator,
          // which makes the condition to be true
Sterling Archer
fonte
8
WTH é uma "declaração falsa"?
21915 Bergi
5
@ Bergi Eu suponho que você realmente saiba o que significa, mas caso você não saiba (ou ninguém mais), aqui está uma explicação para JavaScript que também se traduz um pouco bem em outras línguas com esse conceito (como Python).
21415 Dannnno
11
@Pharap não é minha resposta, então não vou tocá-lo.
21415 Dannnno
2
@ Dannnno: Bem, eu sei o que são valores de falsidade e quais são as declarações , e por isso sei que não existe algo como uma "declaração de falsidade" em JS. Suponho que isso não se refere ao termo lógico .
21915 Bergi
11
Eu não entendo como o --operador poderia trabalhar com uma constante ... talvez você quis dizer em --xvez de --0?
SJuan76
24
if(!--pending)

significa

if(0 == --pending)

significa

pending = pending - 1;
if(0 == pending)
James Turner
fonte
4
E essa é a maneira clara de escrever o código. Claro, prefiro if(0 == pending)por causa do potencial de erros de digitação. if(0 = pending)é um erro de sintaxe. if(pending = 0)é uma atribuição que causará um comportamento confuso no código finalizado.
Theo Brinkman
3
@ TheoBrinkman: na verdade, if(0 = pending)não é um erro de sintaxe - ele analisa bem -, mas um Erro de referência porque 0não é atribuível (ref ECMA-262 (6a ed), seção 12.14.1).
hmakholm sobrou Monica
13

É o operador not seguido pelo pré-decrementador no local.

Então, se pendingera um número inteiro com um valor de 1:

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

Apenas diminui pendingem um e obtém seu complemento lógico (negação). O complemento lógico de qualquer número diferente de 0 é false, para 0 é true.

MinusFour
fonte
Observe que "negar" tem um significado diferente nos números do que nos booleanos
Bergi
11
Tudo bem, eu vou usar um termo diferente. Não tenho certeza se isso é melhor.
MinusFour
11

Explicação

São 2 operadores, um !e um--

!--x 

Portanto, --diminui x por 1, então !retorna true se x agora é 0 (ou NaN ...), false se não for. Você pode ler esse idioma algo como "decrementamos xe se isso o torna zero ..."

Se você quiser torná-lo mais legível, você pode:

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

Experimente:

/* 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 (código de teste)

Ben Aubin
fonte
8

O verdadeiro problema aqui é a falta de espaço entre os dois operadores !e --.

Não sei por que as pessoas pensam que você nunca pode usar um espaço depois do !operador. Eu acho que vem da aplicação rígida de regras de espaços em branco mecânicos em vez do senso comum. Praticamente todos os padrões de codificação que eu vi proíbem os espaços depois de todos os operadores unários, mas por quê?

Se houve um caso em que você claramente precisa desse espaço, esse é um.

Considere este pedaço de código:

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

Não são apenas !e --misturados, você tem que( bater contra eles também. Não é de admirar que seja difícil dizer o que está conectado ao que.

Um pouco mais de espaço em branco torna o código muito mais claro:

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

Claro, se você está acostumado a regras mecânicas como "sem espaço dentro de parênteses" e "sem espaço após um operador unário", isso pode parecer um pouco estranho.

Mas observe como o espaço em branco extra agrupa e separa as várias partes da ifdeclaração e da expressão: você tem --pending, portanto esse --é claramente o seu próprio operador e está intimamente ligado pending. (Ele decrementa pendinge retorna o resultado decrementado.) Depois, você !separa isso e, portanto, é obviamente um operador distinto, negando o resultado. Finalmente, você tem if(e )envolve toda a expressão para torná-la uma ifdeclaração.

E sim, removi o espaço entre ife (, porque ( pertence ao arquivoif . Isso (não faz parte de algum tipo de (!--sintaxe, como parece estar no original, a (parte if da sintaxe da ifprópria declaração.

O espaço em branco aqui serve para comunicar o significado , em vez de seguir algum padrão de codificação mecânica.

Michael Geary
fonte
11
A versão em branco é mais legível para mim. Quando vi pela primeira vez a pergunta que supunha !--ser um operador javascript que eu não conhecia. Por que não usar parênteses para torná-lo ainda mais explícito? if(!(--pending))
Emory
11
Não há guias de estilo estou ciente proíbem o uso ++ou --, mas muitos não proibir utilização de espaços não-essenciais, como após o !operador de prefixo e parênteses não essenciais.
11
O espaço em branco após os operadores unários é desencorajado porque o separa da expressão na qual opera. Você iria querer que ele seja lido em conjunto ... pelo menos imho
aldrin