JavaScript中的"!--"是什么意思?

381

我有这样一段代码(摘自这个问题):

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

我试图理解并遵循它,除了最后的!--pending命令之外,我想我理解了一切。在这种情况下,该命令是做什么用的?

编辑:非常感谢所有进一步的评论,但这个问题已经被回答了很多次。还是谢谢!


你能解释一下这段代码吗?我不明白为什么要检查 if (!--pending) done(null, results); - BlackMamba
66
这让我想起了“-->”运算符的名称是什么? - Soner Gönül
请注意,这里的用例可以通过使用 Promise 轻松解决,而不是发明我们自己的同步机制。此函数也没有正确处理错误。这就是为什么除非你有充分的理由,否则不应该自己编写并发代码的原因。 - Benjamin Gruenbaum
10个回答

542

! 反转一个值,并给出相反的布尔值:

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

--[value] 将一个数字减去一 (1),并将该数字返回以进行后续操作:

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

所以!--pending将从pending中减去1,然后返回它的真/假值的相反值(无论它是否为0)。

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

是的,遵循ProTip。 在其他编程语言中,这可能是一个常见的习语,但对于大多数声明式JavaScript编程来说,这看起来相当陌生。

->

是的,遵循ProTip。在大多数声明式JavaScript编程中,这可能是一个常见的惯用语,但看起来相当陌生。


152

这不是一个特殊运算符,而是两个标准运算符依次出现:

  1. 一个前缀递减 (--)
  2. 一个逻辑非 (!)

这将导致 pending 被递减,然后被测试是否为零。


109
一些答案描述了这个命令做了什么,但没有解释为什么要在此处以这种方式执行。
我来自C世界,读取 `!--pending` 时理解为 "倒数 `pending` 并检查是否为零",却没有深入思考。 我认为类似语言的程序员应该知道这个习惯用法。
该函数使用 `readdir` 获取文件和子目录的列表,我将其统称为 "条目"。
变量 `pending` 跟踪有多少条目尚未处理。 它起始于列表的长度,并随着每个条目的处理而向零计数。
这些条目可能会被无序地处理,这就是为什么需要倒数而不仅仅使用一个简单的循环的原因。 当全部条目都被处理后,回调 `done` 将通知原始调用方此事实。
在第一次调用 `done` 中,前面加上 `return` 不是因为我们想返回一个值,而只是为了使函数在那一点停止执行。 如果去掉 `return` 并将另一种情况放在 `else` 中,则代码会更加清晰。

36

这是一种速记。

! 表示“不是”。

-- 减少一个值。

因此,!-- 检查对一个值进行递减的结果取反是否为 false。

试试这个:

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

第一个是错误的,因为x的值为1,第二个是正确的,因为x的值为0。

顺带一提: !x--会先检查x是否为false,然后再将其减1。


我认为你的意思是"!--返回递减值结果的否定。"只有像console.log()这样的外部代码才会检查其内容是否为真。 - mareoraft

31

!是JavaScript中的运算符。

--是一个预减操作符。因此,

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

24
if(!--pending)

意思是什么

if(0 == --pending)

意思是什么

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

13

这是not运算符后面跟着原位预减量。

因此,如果pending是一个值为1的整数:

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

13

解释

这里有两个操作符,一个是 !,另一个是 --

!--x 

所以,--操作符将x减1,然后!操作符会返回true,如果x现在为0(或NaN...),否则返回false。你可以把这个惯用语读作"我们减小x的值,如果它变成了零..."。
如果你想让它更易读,你可以:
var x = 1
x = x - 1   
if(!x){ //=> true
    console.log("I understand `!--` now!") 
}
x //=> 0

试试看:

/* 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 (试用代码)


11

该操作仅将pending减1,并获取其逻辑补(否定)。任何非0数字的逻辑补是false,对于0则是true


8
这里的真正问题是两个运算符!--之间缺少空格。我不知道为什么人们会认为在!操作符后面不能用空格,我认为这是机械空格规则过于严格,而不是常识。几乎每个编码标准都禁止在所有一元运算符后使用空格,但为什么呢?如果有一个明显需要那个空格的情况,那就是这种情况。考虑一下这段代码:
if (!--pending)
    done(null, results);

不仅!--连在一起,而且(也紧挨着它们。难怪很难分清哪个与哪个相连。

多加一些空格可以使代码更加清晰:

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

当然了,如果你习惯于机械规则,比如“圆括号内不加空格”和“一元运算符后不加空格”,这可能会有点陌生。
但是看看额外的空格是如何将if语句和表达式的各个部分分组和分离的:你有“--pending”,所以"--"显然是它自己的运算符,并与"pending"紧密相连。 (它递减“pending”并返回递减后的结果。) 然后你有一个“!”与之分开,所以它显然是一个独立的运算符,否定结果。 最后,你用“if(”和“)”把整个表达式括起来,使它成为一个“if”语句。
是的,我删除了“if”和“(”之间的空格,因为“(”属于“if”。 这个“(”不是原始代码中似乎是“(! -”语法的一部分,而是if语句本身的语法的一部分。
这里的空格用于传达意义,而不是遵循某些机械化的编码标准。

1
对我来说,带有空格的版本更易读。当我第一次看到这个问题时,我以为 !-- 是一些我不知道的 JavaScript 运算符。为什么不使用括号使其更加明确呢?if(!(--pending)) - emory
1
我所了解的风格指南没有禁止使用++--,但很多指南都禁止使用非必要空格,比如在!前缀运算符后面以及非必要的括号。 - user663031
1
不建议在一元运算符后添加空格,因为它会将其与所操作的表达式分开。您希望它们被一起阅读...至少在我看来是这样。 - aldrin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接