“!-”在JavaScript中有什么作用?


376

我有这段代码(取自这个问题):

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。在这种情况下,该命令有什么作用?

编辑:我感谢所有进一步的评论,但问题已得到解答很多次。不管怎么说,还是要谢谢你!


222
这是混淆下一个人维护代码的绝妙方法。
Eric J.

240
!~--[value] ^ true我称这样的代码为“工作保障”
TbWill4321 2015年


36
@ TbWill4321如果我正在执行代码审查,那将与工作安全性完全相反。
corsiKa

8
运算符与变量名make结合起来还不错。任何经验丰富的Javascript程序员只需几秒钟即可知道它的作用。
克里斯蒂安·韦斯特贝克

Answers:


537

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

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

--[value] 从数字中减去一(1),然后返回要使用的数字:

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

因此,!--pending从未决中减去1,然后返回其true / falsy值的相反值(无论是否为0)。

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

是的,请遵循ProTip。在其他编程语言中,这可能是常见的习惯用法,但是对于大多数声明性JavaScript编程而言,这似乎很陌生。


623
ProTip™:永远不要在您的代码中这样做,这太荒谬了。
Naftuli Kay 2015年

17
这没有提到--仅作用于变量;它没有作用于变量。您一般无法将其应用于值。
deltab

10
@deltab:确切地说validSimpleAssignmentTarget是s。这包括标识符引用和属性引用。
Bergi 2015年

19
--0而且--1行不通。数字文字不是有效的左侧表达式
PSWai 2015年

7
@Pharap:嗯,我解析该i > -1问题要比i--(而且您忘了i = init() - 1)要麻烦得多。这就是成语的意思……每个程序员都应该学习它们。
Bergi 2015年

149

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

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

这将导致pending递减,然后进行测试以查看其是否为零。


8
我绝对是新手,但是为什么这比使用更好if(pending === 1)
基兰E

46
@KieranE,不是,它是速记法,完全打破了“可读性为王”的规则。
瑞安

23
@KieranE不是要出众,而是要有所不同。它变异变量(将其减1),然后使用新值进行测试
Amit

7
@KieranE,它等效于if( pending === 1 ) { done... } else { pending = pending - 1; }
芯片

7
@CompuChip实际上看起来---pending在任何情况下都可以完成,因此代码更像是:if(!等等}其他{-待定;等}这是基于developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
保罗·拉塞尔

109

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

我来自C语言世界,读!--pending为“递减pending并检查是否为零”,而没有真正考虑它。我认为类似语言的程序员应该知道这是一个习惯用法。

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

该变量pending跟踪要处理的剩余数量。它从列表的长度开始,并在处理每个条目时向下计数到零。

这些条目可能会乱序处理,这就是为什么有必要倒数而不是仅使用简单循环的原因。当全部的项目都已经被处理的回调done被调用,通知这个事实原始调用者。

在第一个调用中以done开头return,不是因为我们要返回一个值,而是简单地使函数在那一点上停止执行。删除return并将替代项放入else


13
@Bergi是C世界中一个众所周知的习语,但它不是惯用的Javascript。对于任何给定的子表达,不同的生态系统将具有各种不同的,不兼容的习语,以及在那里的“完成方式”。使用来自“外来”语言的成语是一种很不好的代码味道。
彼得尼斯(Peteris),2015年

3
@Peteris:这是所有带有减量运算符的C语言的惯用语。当然,有时在语言中可以使用其他更好的方法,但是我认为JS没有任何特殊的计数习惯用法。
Bergi 2015年

6
Javascript社区中有很大一部分,包括像Douglas Crockford这样有影响力的领导者,他们主张完全避免使用一元递增和递减运算符。我不会在这里争论它们是否正确。我的意图仅是指出,由于存在争议,像这样的代码!--pending将使许多短毛绒和样式准则失败。这足以让我说这可能不是惯用的(无论替代方案是否“更好”)。
GrandOpener

3
@GrandOpener“惯用语”的意思是“母语人士通常会理解的表达”。对于使用C语言的人来说,先决肯定是很容易理解的,因此,“!(-x)”也是如此。无论使用什么是一个好IDEA®是一个单独的问题完全由习惯是怎么样。(例如,我非常怀疑您会遇到许多不懂“ goto”功能的语言的程序员,尽管经常有人认为将其包含在代码中属于“ Lust”和“ Murder”之间八种致命的罪恶中的一种。)
jmbpiano

3
@Pharap那是因为C#和Java不会隐式转换intbool,并且与的使用完全无关!--
Agop 2015年

36

这是简写​​。

! 不是”。

-- 递减值。

因此,请!--检查否定值减结果所获得的值是否为假。

尝试这个:

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

第一个为false,因为x的值为1,第二个为true,因为x的值为0。

旁注: !x--首先检查x是否为假,然后将其递减。


请注意-您确定吗?看起来,修复后的优先级比更高!,而修复后的优先级更低。JavaScript运算符优先级
Dannnno 2015年

2
是。(尝试var x = 2; console.log(!x--); console.log(!x--); console.log(!x--);)。虽然--可能首先执行后缀,但其返回值是递减之前的变量值(Decrement Opperator)。
卢卡斯

我认为您的意思是说“ !--返回值递减结果的否定”。仅外部代码(例如console.log())检查其内容是否真实。
mareoraft,2015年

31

!是JavaScript NOT运算符

--是递减运算符。所以,

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

8
什么是“虚假陈述”?
Bergi 2015年

5
@Bergi我假设您实际上知道它的意思,但是如果您不知道(或其他人不知道),这里是JavaScript的解释,它也可以将此概念很好地翻译为其他语言(例如Python)。
Dannnno 2015年

1
@Pharap这不是我的答案,所以我不会去碰它。
Dannnno 2015年

2
@Dannnno:好吧,我知道什么是虚假值和什么语句,所以我知道在JS中没有像“虚假语句”这样的东西。我认为这与逻辑术语无关。
Bergi 2015年

1
我不明白--运算符如何以常数工作……也许您是想--x代替--0
SJuan76

24
if(!--pending)

手段

if(0 == --pending)

手段

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

4
是写代码的明确的方式。当然,我更喜欢if(0 == pending)打字错误的可能性。 if(0 = pending)是语法错误。 if(pending = 0)是一种分配,将导致最终代码中的行为混乱。
西奥·布林克曼

3
@TheoBrinkman:实际上if(0 = pending)不是语法错误,它可以很好地解析,但是由于0没有可分配的原因而导致了参考错误(请参阅ECMA-262(第6版)第12.14.1节)。
hmakholm在莫妮卡(Monica)遗留在2015年

13

它是非运算符,之后是就地预减数器。

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

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

11

它仅减少pending一个,并获得其逻辑补(负)。不同于0的任何数字的逻辑补码为false,对于0则为true


请注意,“取反”在数字上的含义与布尔值的含义不同
Bergi 2015年

1
好吧,我将使用另一个术语。不知道这是否更好。
MinusFour 2015年

11

说明

这是2个运算子,a !和a--

!--x 

因此,将--x减1,然后!如果x现在为0(或NaN ...),则返回true,否则返回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>

小提琴(试用代码)


8

真正的问题在于,两个运算符!和之间没有空格--

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

如果曾经有明显的需求,那就是一个。

考虑一下这段代码:

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

不仅是!--一起捣碎,你已经得到了(撞着他们。难怪很难分辨什么与什么相关。

更多的空格使代码更清晰:

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

当然,如果您习惯于使用机械规则,例如“括号内没有空格”和“一元运算符之后没有空格”,那么这似乎有点陌生。

但是,请看一下额外的空白如何将if语句和表达式的各个部分进行分组和分隔:您已经了解了--pending,所以--显然是它自己的运算符,并且与紧密相关pending。(它递减pending并返回递减的结果。)然后,您将其与之!分开,因此它显然是一个独特的运算符,对结果进行求反。最后,你得if()周围的整体表现,使它的if声明。

是的,我删除之间的空间if(,因为( 所属if。这(不是某种(!--语法的一部分,因为它似乎是原始(语法(如果是if语句本身的语法的if部分)。

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


1
空白版本对我来说更易读。当我第一次看到这个问题时,我认为!--是我不知道的一些javascript运算符。为什么不使用括号使之更加明确?if(!(--pending))
2015年

1
没有风格指南我知道禁止使用++--,但很多使用非必要的空间,如后禁止!前缀运营商,和非必需的括号。

1
不建议使用一元运算符后的空格,因为它会将其与运算符分开。您希望将其阅读在一起……至少是恕我直言
aldrin
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.