什么"!——"用JavaScript做什么?

下面这段代码(取自这个问题):

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。在这个上下文中,该命令做什么?

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

57007 次浏览

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

  1. 前缀递减(--)
  2. 逻辑not (!)

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

!是JavaScript的操作符

--是一个预递减操作符。所以,

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

这是一种速记。

!是“not”。

--递减一个值。

因此!--检查从一个值的递减结果求负得到的值是否为假。

试试这个:

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

第一个为假,因为x的值是1,第二个为真,因为x的值是0。

< p >注: !x--将首先检查x是否为假,然后减去它

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

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

--[value]从一个数字中减去1(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编程来说,这看起来相当陌生。

它是not运算符后面跟着原地预减数。

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

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

它只是将pending减1,并获得其逻辑补(否定)。任何不同于0的数字的逻辑补是false,因为0是true

许多答案描述了什么这个命令,但不是为什么,它在这里是这样做的。

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

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

变量pending记录了有多少剩余的数据需要处理。它开始时是列表的长度,随着每个条目的处理,它向下计数到零。

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

在第一次调用done时,前面有return,这并不是因为我们想要返回一个值,而只是为了让函数在那一点停止执行。删除return并将替代项放在else中会更干净。

if(!--pending)

意味着

if(0 == --pending)

意味着

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

这里真正的问题是两个操作符!--之间缺少空格。

我不知道为什么人们总是认为!操作符后面不能有空格。我认为它来自于机械空白规则的僵化应用,而不是常识。我所见过的几乎所有编码标准都禁止在一元运算符之后使用空格,但为什么呢?

如果有一种情况,你清楚地需要空间,这是一个。

考虑这段代码:

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

不仅!--被混在一起,你还把(撞在了一起。难怪很难分辨什么和什么有关。

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

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

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

但是看看额外的空格是如何分组和分离if语句和表达式的各个部分的:你已经有了--pending,所以--显然是它自己的操作符,并与pending紧密联系在一起。(它递减pending并返回递减后的结果。)然后你将!与它分离,所以它显然是一个不同的操作符,对结果求反。最后,你有if()包围整个表达式,使其成为if语句。

是的,我删除了if(之间的空格,因为( 属于if。这个(不是某种(!--语法的一部分,因为它看起来是原始的,(if语句本身语法的一部分。

这里的空格用于传递意义,而不是遵循一些机械的编码标准。

解释

这是两个操作符,一个!和一个--

!--x

因此,--将x减1,然后如果x现在为0(或NaN…),则!返回true,如果不是则返回false。你可能会把这个习语理解为“我们递减x,如果它变成0……”

如果你想让它更具可读性,你可以:

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 (Try Out Code)