为什么要避免 JavaScript 中的递增(“++”)和递减(“--”)运算符?

IT技术 javascript syntax jslint postfix-operator prefix-operator
2021-02-09 00:39:45

jslint 工具提示之一是:

++ 和 --
++(递增)和 --(递减)运算符通过鼓励过度的技巧而导致糟糕的代码。在导致病毒和其他安全威胁方面,它们仅次于有缺陷的架构。有一个 plusplus 选项禁止使用这些运算符。

我知道像这样的 PHP 构造$foo[$bar++]可能很容易导致一个错误,但我想不出比 awhile( a < 10 ) do { /* foo */ a++; }更好的方法来控制循环for (var i=0; i<10; i++) { /* foo */ }

jslint 是否突出显示了它们,因为有一些类似的语言缺少“ ++”和“ --”语法或以不同的方式处理它,或者是否有其他理由可以避免我可能遗漏的++”和“ --”?

6个回答

我的观点是始终在一行中单独使用 ++ 和 -- ,如下所示:

i++;
array[i] = foo;

代替

array[++i] = foo;

除此之外的任何事情都会让一些程序员感到困惑,在我看来是不值得的。For 循环是一个例外,因为增量运算符的使用是惯用的,因此总是很清楚。

是的,我也是这样做的。很难出错,而且其他人更容易弄清楚你在做什么。
2021-03-14 00:39:45
同意,但这不仅与 JavaScript 相关
2021-03-23 00:39:45
你不会说你是在写 C 还是 C++。可以说,您应该在 C++ 中使用前缀增量运算符,以防i变量稍后成为复合类,在这种情况下,您将不必要地创建临时对象。不过,我发现后缀运算符在美学上更令人愉悦。
2021-03-28 00:39:45
我更喜欢 1-line 版本。它让人想起堆栈操作。push 是 array[++i] = foo,pop 是 bar = array[i--]。(但是对于基于 0 的数组,array[i++] = foo 和 bar = array[--i] 看起来更好)。另一件事,如果有人在 i++ 之间添加额外的代码,我会讨厌它;和数组[i] = foo;。
2021-04-02 00:39:45
这似乎是问题的核心和我的困惑。单独使用,i++;晶莹剔透。您围绕该基本表达式添加其他代码的那一刻,可读性和清晰度开始受到影响。
2021-04-10 00:39:45

坦率地说,我对这个建议感到困惑。我的一部分想知道这是否与缺乏 javascript 编码人员的经验(感知或实际)有关。

我可以看到有人只是“破解”一些示例代码可能会在使用 ++ 和 -- 时犯下无辜的错误,但我不明白为什么有经验的专业人士会避免使用它们。

我完全不同意你的回答。在我看来,这不是关于“我是否有足够的经验来使用它?” - 但“是否更清楚?” 如果在 for 循环中或单独使用,这是非常清楚的 - 每个人都会明白这一点,而不会造成任何伤害。++在 ++x 与 x++ 不同的更复杂的表达式中放入更复杂的表达式时,问题就出现了,从而导致一些不容易阅读的东西。克罗克福德的想法不是关于“我能做到吗?” 它是关于“我怎样才能避免错误?”
2021-03-21 00:39:45
@artlung 也许它更多地与“一个懒惰和不专心的没有经验的黑客”有关,可能会弄乱这段代码,我不想混淆他。
2021-03-24 00:39:45
我完全同意。为什么不使用部分语言来妨碍自己?我找到了使用 post- 和 pre- 增量和减量的充分理由。对我来说,像这样的一行清晰简洁...... if (--imagesLoading === 0) start(); 如果您认为您的代码不清楚,请使用注释!
2021-03-26 00:39:45
在哪一点上清晰比紧凑更重要是个人偏好。这不仅仅是有足够的经验来理解代码。请参阅其他答案以获取比其value更麻烦的示例。
2021-04-06 00:39:45
好点 Jon B。这就是为什么它让我如此困扰。Crockford 是一名职业程序员(几十年),几十年来精通多种语言,并且从 JavaScript 开始就基本上使用它。我不认为他的建议来自“我是个懒惰且不专心的没有经验的黑客”——这就是为什么我试图了解它的来源。
2021-04-08 00:39:45

在 C 中有做这样的事情的历史:

while (*a++ = *b++);

复制一个字符串,也许这就是他所指的过度诡计的来源。

总是有什么问题

++i = i++;

或者

i = i++ + ++i;

实际上做。它在某些语言中定义,而在其他语言中则无法保证会发生什么。

撇开这些例子不谈,我认为没有什么比用于++递增的 for 循环更惯用了在某些情况下,您可以使用 foreach 循环或检查不同条件的 while 循环。但是为了避免使用递增而扭曲您的代码是荒谬的。

唉,(a++; b++; x=a=b) 与 (a++ + ++b) 的值不同。
2021-03-14 00:39:45
@Marius: x = a+++b--> x = (a++)+b--> x = a + b; a++标记器是贪婪的。
2021-03-18 00:39:45
为了额外的工作安全,请删除上一个示例中的空格。
2021-03-19 00:39:45
i = i++ + ++i; 让我的眼睛有点痛 - :)
2021-03-24 00:39:45
我也是。即使它们不是完全相同的变量,假设我有 x = a++ + ++b; - 这种组合立即让 x、a 和 b 更难以在我的脑海中记忆。现在,如果每个语句在不同的行上都是单独的语句,例如 -- a++; ++b; x = a + b; 乍一看会更容易理解。
2021-04-08 00:39:45

如果您阅读 JavaScript The Good Parts,您会看到 Crockford 在for循环中对 i++ 的替换是 i+=1(而不是 i=i+1)。这是非常干净和可读的,并且不太可能变成“棘手”的东西。

Crockford 将不允许自动递增和自动递减作为 jsLint 中的一个选项您可以选择是否遵循建议。

我个人的规则是不要做任何与自动递增或自动递减相结合的事情。

我从多年的 C 经验中了解到,如果我继续简单地使用它,我不会遇到缓冲区溢出(或数组索引越界)。但是我发现如果我陷入在同一语句中做其他事情的“过于棘手”的做法,我确实会遇到缓冲区溢出。

因此,对于我自己的规则,使用 i++ 作为for循环中的增量是可以的。

关于“plusplus”选项的优秀之处就是一个选项。根据您和其他答案,我想我觉得 ++ 本身或 for 循环本身并不令人困惑。第二次将 ++ 组合到另一个语句中,您的 r 语句在上下文中变得更加难以阅读和理解。
2021-03-20 00:39:45
每个人都会遇到缓冲区溢出。甚至 OpenSSH 及其开源和多个修订委员会
2021-03-22 00:39:45
@Eric 重温你五岁的评论:哦,你是多么正确!
2021-04-05 00:39:45

在循环中它是无害的,但在赋值语句中它可能导致意想不到的结果:

var x = 5;
var y = x++; // y is now 5 and x is 6
var z = ++x; // z is now 7 and x is 7

变量和运算符之间的空格也会导致意外结果:

a = b = c = 1; a ++ ; b -- ; c; console.log('a:', a, 'b:', b, 'c:', c)

在闭包中,意外的结果也可能是一个问题:

var foobar = function(i){var count = count || i; return function(){return count++;}}

baz = foobar(1);
baz(); //1
baz(); //2


var alphabeta = function(i){var count = count || i; return function(){return ++count;}}

omega = alphabeta(1);
omega(); //2
omega(); //3

它会在换行符后触发自动分号插入:

var foo = 1, bar = 2, baz = 3, alpha = 4, beta = 5, delta = alpha
++beta; //delta is 4, alpha is 4, beta is 6

前增量/后增量混淆会产生极难诊断的逐一错误。幸运的是,它们也完全没有必要。有更好的方法将 1 添加到变量中。

参考

您的空白链接似乎已损坏。
2021-03-11 00:39:45
我认为 Crockford 的观点是,大多数程序员即使认为他们理解,也没有完全“理解操作符”;因此,使用它们存在危险,因为误解的可能性很高。请参阅@Paul 关于错误假设的评论,该评论支持人们通常误解前缀和后缀运算符实际工作方式的论点。也就是说,我承认我喜欢使用它们,但为了其他人,我尝试将它们的使用限制在惯用的情况下(请参阅cdmckay 的回答)。
2021-03-14 00:39:45
您的“空白”示例有什么棘手之处?我得到一个直接的输出a: 2 b: 0 c: 1在第一个示例(“赋值语句”)中,我也没有看到任何奇怪或意外的东西。
2021-03-21 00:39:45
确切地。有一个C# 问题可以揭穿错误的假设。
2021-03-29 00:39:45
但是,如果您了解运算符,它们就不会“出乎意料”。这就像没有使用过==,因为你不明白之间的差别=====
2021-04-09 00:39:45