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

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

不仅是!然后——把它们搅在一起,你就得到了(它们也被撞到了一起)。难怪很难分辨什么和什么有关。

多一点的空格使代码更加清晰:

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

当然,如果你已经习惯了像“双括号内没有空格”和“一元操作符后没有空格”这样的机械规则,这可能看起来有点陌生。

但是,看看额外的空格是如何分组和分离if语句和表达式的各个部分的:您已经有了——pending,因此——显然是它自己的操作符,并且与pending密切相关。(它递减pending并返回递减后的结果。)然后你就得到了!与它分开,所以它显然是一个不同的运算符,对结果求负。最后,在整个表达式周围加上if(和),使其成为if语句。

是的,我去掉了if和(之间的空格,因为(属于if。这不是某种(!)if语句本身语法的(if)部分。

这里的空白用于传达含义,而不是遵循一些机械的编码标准。

! JavaScript是NOT操作符吗

——是一个预自减运算符。所以,

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

这不是一个特殊运算符,它是两个标准运算符一个接一个:

前缀递减(——) 一个合乎逻辑的不(!)

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

它只是把未决减一,而得到它的逻辑补足(否定)。任何不同于0的数的逻辑补均为假,因为0为真。

许多答案描述了这个命令的功能,但没有说明为什么要这样做。

我来自C语言的世界,我读!- pending的意思是“倒数pending,检查它是否为零”,而不是真正地思考它。我认为使用类似语言的程序员应该知道这个习语。

该函数使用readdir获取文件和子目录列表,我将它们统称为“条目”。

变量pending会跟踪这些数据中还有多少需要处理。它开始时是列表的长度,随着每个条目的处理,它向下计数到零。

这些条目可能会被无序地处理,这就是为什么有必要进行倒数,而不是仅仅使用一个简单的循环。处理完所有条目后,将调用回调done以通知原始调用方这一事实。

在第一次调用done之前加上return,并不是因为我们想要返回一个值,而只是为了让函数在那一刻停止执行。如果放弃返回并将替代方法放在else中,代码会更简洁。