jslint工具的技巧之一是:


++和-
已知++(递增)和-(递减)
运算符通过鼓励过度的棘手行为而为错误的代码做出贡献。它们
仅次于错误的体系结构
,从而使病毒和其他安全威胁成为可能。有一个plusplus
选项,禁止使用这些
运算符。


我知道像$foo[$bar++]这样的PHP构造很容易因一一对应而导致错误,但我找不到比while( a < 10 ) do { /* foo */ a++; }for (var i=0; i<10; i++) { /* foo */ }更好的方法来控制循环。

jslint突出显示它们是因为有些类似的语言缺少“ ++”和“ --” ”语法或以其他方式处理它,或者是否有其他理由来避免可能会丢失的“ ++”和“ --”?

评论

因此,应该执行array [index = index + 1]而不是array [++ index](如果允许前者!)。哎呀,真是累死了。

我还没有看到克罗克福德做index = index + 1。我看过他做index + = 1。我认为这是一个合理的选择。当您想将跨度更改为1以外的其他内容时,这非常有用。

我个人不是Crockford的忠实粉丝。他似乎认为任何导致其代码错误的内容都是邪恶的。

在JavaScript中,您应该将每个错误都视为邪恶的,因为没有官方文档,也没有证书提供程序,或者您在大学中无法正确学习JS。 Crockford和Firebug在JavaScript教学中填补了这些空白。

++不会导致错误。以“棘手的”方式使用++可能会导致错误,尤其是如果有多个人维护代码库时,但这不是操作员的问题,而是程序员的问题。我没有在大学学习JS(因为它还不存在),那又如何呢?我做了C,当然先是++,但是那也得到了“那又怎样?”我没有上大学来学习特定的语言,而是去学习可以应用于任何语言的良好编程习惯。

#1 楼

我的观点是始终始终在一行上单独使用++和-,例如:

i++;
array[i] = foo;


而不是

array[++i] = foo;


除此之外,任何其他事情都可能使某些程序员感到困惑,而且我认为这不值得。 For循环是一个例外,因为增量运算符的使用是惯用的,因此始终很清楚。

评论


是的,那也是我要做的。很难出错,其他人更容易弄清楚您在做什么。

– Nosredna
2009年6月9日在17:46

这似乎已成为问题和我的困惑的核心。单独使用,i ++;很清晰。在围绕该基本表达式添加其他代码的那一刻,可读性和清晰度开始受到影响。

– artlung
09年9月9日在17:54

同意,但这不仅与JavaScript有关

– Tobias
13年2月4日在13:37

您没有说要编写C还是C ++。可以争论的是,应该在C ++中使用前缀增量运算符,以防i变量后来变成复合类,在这种情况下,您将不必要地创建一个临时对象。我发现后缀运算符在美学上更令人愉悦。

–mc0e
2014年1月11日在8:22



我更喜欢1行版本。它提醒了堆栈操作。 push是array [++ i] = foo,pop是bar = array [i--]。 (但是对于基于0的数组,array [i ++] = foo和bar = array [-i]看起来仍然更好)。另一件事,如果有人在i ++之间添加了额外的代码,我会讨厌它。和array [i] = foo;。

– Florian F
16年6月22日在21:38



#2 楼

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

我可以看到有人只是“ hacking”一些示例代码会如何用++和-犯一个无辜的错误,但是我不明白为什么有经验的专业人员会避免使用它们。 />

评论


乔恩·B(Jon B)好点了。这就是为什么它令我如此困扰。 Crockford是一位职业程序员(数十年身价),数十年来都使用多种语言进行交流,并且从创建以来就基本上使用JavaScript。我认为他的建议不是来自“我是个懒惰且缺乏专心的经验不足的黑客”,这就是为什么我试图了解它的来源。

– artlung
09年9月9日在17:22

@artlung也许与“一个懒惰且不专心的经验不足的黑客”有关,这可能会使此代码混乱,并且我不想混淆他。

–克里斯·库德莫(Chris Cudmore)
2010年7月13日在15:21

我完全不同意你的回答。我认为这不是关于“我是否有足够的经验来使用它?”。 -但是“清楚吗?”如果是在for循环中,或者是一个循环,那么就很清楚了-每个人都可以理解而无害。当把++放在更复杂的表达式中时,问题就来了,而++ x与x ++有所不同,从而导致某些内容不易阅读。克罗克福德的想法不是关于“我能做到吗?”这是关于“如何避免错误?”

– ArtoAle
2012-09-18 22:37

在什么时候,清晰度对您来说比紧凑性更为重要,这是个人喜好。这超出了足以理解代码的经验。请参阅其他答案,以获取更多麻烦而不是其价值的示例。

–节俭
2012年10月1日19:37

我完全同意。为什么不使用部分语言来妨碍自己?我发现同时使用后增量和预增量以及减量的充分理由。对我来说,这样的行是简洁明了的... if(--imagesLoading === 0)start();如果您认为代码不清楚,请使用注释!

–xtempore
2012年11月20日5:58



#3 楼

C语言中有一个类似的历史记录:

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


复制字符串,这也许就是他所指的过多欺骗行为的根源。

总是存在什么问题

++i = i++;




i = i++ + ++i;


确实可以。它是在某些语言中定义的,而在其他语言中则不能保证会发生什么。

除了这些示例,我认为没有什么比使用++递增的for循环更惯用的了。在某些情况下,您可以放弃使用foreach循环,或使用while循环检查其他条件。但是扭曲代码以尝试避免使用增量是荒谬的。

评论


i = i ++ + ++ i;使我的眼睛有点受伤-:)

–Hugoware
2009年6月9日在17:38

我也是。即使它们不是相同的变量,也可以说我有x = a ++ + ++ b; -这种组合立即使x,a和b难以固定在我的脑海中。现在,如果每个都是在不同行上的单独语句,例如-a ++; ++ b; x = a + b;乍一看会更容易理解。

– artlung
2009年6月9日在17:46

las,(a ++; b ++; x = a = b)与(a ++ + ++ b)的值不同。

– Thomas L Holaday
09年9月9日在20:48

@Marius:x = a +++ b-> x =(a ++)+ b-> x = a + b; a ++。分词器贪婪。

–卡勒姆·罗杰斯(Callum Rogers)
2010-09-27 13:31



为了提高工作安全性,请删除上一个示例中的空格。

–mc0e
2014年1月11日在8:28

#4 楼

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

Crockford在jsLint中将禁止自动递增和自动递减作为选项。您可以选择是否遵循建议。

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

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

所以,按照我自己的规则,将i ++用作for循环中的增量很好。

评论


关于“ plusplus”选项的要点就是,这是一个选项。根据您的回答和其他答案,我认为我已经意识到++本身或for循环本身并不会造成混淆。第二个将++组合到另一个r语句中的语句在上下文中变得更加难以阅读和理解。

– artlung
09年9月9日在17:43

每个人都有缓冲区溢出。甚至OpenSSH及其开源和多个修订委员会

–埃里克
2009年6月10日14:22

@Eric回顾您五岁的评论:哦,您是多么正确!

– wchargin
2015年2月3日,0:31



#5 楼

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

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





参考文献


JSLint帮助:增量和减量运算符


评论


但是,如果您了解操作员,他们并不是“意外”。就像从未使用过==一样,因为您不了解==和===之间的区别。

– greg84
2013年1月19日14:10

究竟。有一个C#问题揭穿了错误的假设。

–Paul Sweatte
13年1月19日在17:13

我认为Crockford的观点是,即使大多数程序员认为自己确实做到了,他们也不是完全“了解操作员”。因此,使用它们存在危险,因为误解的可能性很高。参见@Paul关于错误假设的评论,该评论支持人们普遍误解前缀和后缀运算符实际工作方式的论点。就是说,我承认我喜欢使用它们,但是为了其他目的,我试图将它们的使用限制为惯用案例(请参阅cdmckay的回答)。

– gfullam
2015年7月9日15:11

您的空白链接似乎已损坏。

–俄罗斯
19年5月5日,11:55



您的“空白”示例有什么棘手的问题?我得到了以下结果的直接输出:2 b:0 c:1.在第一个示例(“赋值语句”)中,我也看不到任何奇怪或意外的东西。

–肯·威廉姆斯
19-10-9在21:04

#6 楼

考虑下面的代码

    int a[10];
    a[0] = 0;
    a[1] = 0;
    a[2] = 0;
    a[3] = 0;
    int i = 0;
    a[i++] = i++;
    a[i++] = i++;
    a[i++] = i++;


,因为i ++得到了两次输出评估,结果是
(来自vs2005调试器)

    [0] 0   int
    [1] 0   int
    [2] 2   int
    [3] 0   int
    [4] 4   int


现在考虑以下代码:

    int a[10];
    a[0] = 0;
    a[1] = 0;
    a[2] = 0;
    a[3] = 0;
    int i = 0;
    a[++i] = ++i;
    a[++i] = ++i;
    a[++i] = ++i;


请注意输出是相同的。现在您可能会认为++ i和i ++是相同的。它们不是

    [0] 0   int
    [1] 0   int
    [2] 2   int
    [3] 0   int
    [4] 4   int


最后考虑此代码

    int a[10];
    a[0] = 0;
    a[1] = 0;
    a[2] = 0;
    a[3] = 0;
    int i = 0;
    a[++i] = i++;
    a[++i] = i++;
    a[++i] = i++;


输出现在是:

    [0] 0   int
    [1] 1   int
    [2] 0   int
    [3] 3   int
    [4] 0   int
    [5] 5   int


所以它们并不相同,将两者混合会导致不太直观的行为。我认为for循环对于++来说还可以,但是当您在同一行或同一条指令上有多个++符号时要当心

评论


感谢您进行该实验。特别是“外观简单”的代码,但是我的大脑很难很快抓住它。绝对属于“棘手”类别。

– artlung
09年9月9日在18:08

我停在第一个例子之后。您说“请考虑以下代码”。不,没人写那个代码。是谁在骗谁,而不是语言建构的白痴。

–山姆·哈威尔(Sam Harwell)
09年8月5日在21:51

您已经证明了两个不同的代码段会产生不同的输出,这证明它不好吗?

–nickf
09年8月30日在6:45

结论是“当在同一行或同一指令上有多个++符号时要当心”。我不认为你读过

–埃里克
09年8月30日在7:07

埃里克(Eric),我看不到您要达到的目标。该代码产生的结果恰好是人们期望的。这里没有什么是反直觉的。

–洛伦茨·罗索尔
2011年10月3日,12:13

#7 楼

增量和减量运算符的“前”和“后”性质可能会使那些不熟悉它们的人感到困惑。这是他们变得棘手的一种方式。

评论


同意;但是,经验丰富的专业人员不应混淆。

–内特
09年9月9日在17:11

我认为这类似于避免将所有内容设为静态或全局的指导。作为编程结构,它没有内在的错误,但是无知的过度使用会导致错误的代码。

–乔尔·马丁内斯(Joel Martinez)
09年9月9日在17:16

问题不在于优秀的程序员是否可以通过逻辑“按自己的方式工作”,理想情况下,好的代码不需要任何工作即可理解。我认为McWafflestix的观点只是,您不应编写经过短暂检查就可能导致在两周或两年内添加错误的代码。我知道我对向我不太了解的例程添加代码感到内::)

–迈克
09年9月9日在17:17

@Mike:是的,请注意我没有指定操作符对于代码的原始作者还是对以后的维护者而言可能是棘手的。通常,我们尝试假设原始编码人员不使用他们不熟悉/不熟悉的构造(可悲的是,这通常是一个错误的假设);但是,这种熟悉度当然不会扩展到维护者(并且,如上文附带的注释所示,甚至可能不会扩展到原始作者)。

–Paul Sonier
09年9月9日在17:26

我试图写我的代码供其他程序员阅读,但我不尝试写它供初学者阅读。

–卢克·H
13年5月5日在10:42



#8 楼

在我看来,“显式总比隐式好。”因为在某些时候,您可能会对这个增量语句y+ = x++ + ++y感到困惑。一个好的程序员总是使他或她的代码更具可读性。

评论


x ++或++ y中没有隐式内容。

– Florian F
16年7月19日在14:23

这种可读的代码是棘手的……我认为您的代码必须有多么简单是一条基线,作为一个社区,我们必须蓬勃发展,并且能够阅读现在不可读的内容,例如单线带有嵌套的三元组,以允许添加更多内容。

– Hamza Ouaghad
16年8月31日在12:15



#9 楼

避免++或-的最重要理由是,运算符会同时返回值并引起副作用,这使得对代码进行推理变得更加困难。

出于效率考虑,我更喜欢:



++ i不使用返回值(非临时)时

i ++使用返回值时(没有管道停顿)

我是Crockford先生的粉丝,但是在这种情况下,我必须不同意。 ++ii+=1解析的文本少25%,而且可以说更清晰。

#10 楼

我一直在看道格拉斯·克罗克福德(Douglas Crockford)的视频,他对不使用增量和减量的解释是,


它在过去已被其他语言用于打破数组的界限和导致各种不良后果,并且
这更加令人困惑,而且经验不足的JS开发人员也不知道它到底在做什么。

首先,JavaScript中的数组是动态调整大小的,因此,如果我错了,请原谅我,不可能打破数组的界限并访问不应使用此方法访问的数据JavaScript。

其次,我们应该避免复杂的事情吗?问题肯定不是我们拥有此功能,而是问题在于那里的开发人员声称可以使用JavaScript,但不知道这些如何运营商的工作??这很简单。 value ++,给我当前值,然后在表达式中添加一个值,++ value,在给我之前增加值。

像++ + ++ b这样的表达式很简单如果您只记得上面的内容,那就知道了。

var a = 1, b = 1, c;
c = a ++ + ++ b;
// c = 1 + 2 = 3; 
// a = 2 (equals two after the expression is finished);
// b = 2;


如果您的团队了解JS,我想您应该记住是谁必须通读代码由内而外,那么您不必担心。如果没有,请对其进行注释,然后以不同的方式编写,等等。我不认为递增和递减本质上不会造成不良影响,错误生成或漏洞的产生,取决于您的听众,它们的可读性可能较低。

顺便说一句,我认为道格拉斯·克罗克福德还是一个传奇人物,但我认为他为一个不该当的操作员造成了很多恐慌。

尽管如此,我仍然被证明是错误的...

#11 楼

另一个示例,比其他一些示例更简单,可以简单地返回增值后的值:

function testIncrement1(x) {
    return x++;
}

function testIncrement2(x) {
    return ++x;
}

function testIncrement3(x) {
    return x += 1;
}

console.log(testIncrement1(0)); // 0
console.log(testIncrement2(0)); // 1
console.log(testIncrement3(0)); // 1


如您所见,return语句不应使用后增/减,如果希望此运算符影响结果。但是return不会“捕获”后增/减运算符:

function closureIncrementTest() {
    var x = 0;

    function postIncrementX() {
        return x++;
    }

    var y = postIncrementX();

    console.log(x); // 1
}


评论


您的函数中只有一个接收x作为参数

–Calimero100582
13-10-15在19:30

#12 楼

我认为程序员应该在使用的语言方面胜任。清楚地使用它;并很好地使用它。我认为他们不应该人为地削弱所使用的语言。我从经验上讲。我曾经在Cobol商店的隔壁工作过,在那里他们不使用ELSE,因为那太复杂了。 Reductio ad absurdam。

评论


@downvoter令人着迷。您认为程序员不应该精通这种语言吗?您认为他们应该人为削弱语言吗?哪有这里没有第三选择。

–user207421
13年7月16日在10:29

对那个COBOL示例的支持使我更加高兴的是,在我开始编码之前,COBOL几乎全部消亡了

– Mark K Cowan
15年2月28日在13:21

@MarkCowan我不明白为什么。我的意思是关于任意限制,而不是语言。轶事并不表明Cobol有任何问题。需要消亡的是那家编程商店,事实确实如此。参观任何大型银行的内幕,您会发现Cobol仍然很健康。

–user207421
15年2月28日在20:30

#13 楼

以我的经验,++ i或i ++从来没有引起混乱,除非是第一次了解运算符的工作原理。对于最高级的for循环和while循环,这是必不可少的,在任何高中或大学课程中,只要您可以使用运算符就可以教这些语言。我个人觉得做下面的事情要比使用单独的一行的a ++更好地阅读和阅读。




 while ( a < 10 ){
    array[a++] = val
} 





最后,这是一种样式首选项,仅此而已,更重要的是,当您在代码中执行此操作时,请保持一致,以便其他从事同一代码的人员可以遵循并且不必以不同的方式处理相同的功能。

此外,Crockford似乎使用i- = 1,我发现它比--i或i--难于理解。

#14 楼

正如一些现有答案(令人烦恼的是,我无法对此发表评论)中所提到的那样,问题在于x ++ ++ x计算得出的值不同(在增量之前与之后),这并不明显,并且可能非常令人困惑-如果使用该值。 cdmckay非常明智地建议允许使用增量运算符,但只能采用不使用返回值的方式,例如在自己的路线上。我还将在for循环中包括标准用法(但仅在未使用其返回值的第三条语句中)。我想不出另一个例子。自己被“烧死”后,我会建议其他语言也使用相同的指南。

我不同意这种严格要求是由于许多JS程序员缺乏经验的说法。这是“聪明的”程序员典型的写法,我敢肯定,它在更传统的语言中以及在具有这种语言背景的JS开发人员中更为常见。

评论


谢谢回复。您需要信誉度为50或更高才能发表评论。请参阅:stackoverflow.com/faq

– artlung
11年1月27日在17:08

@artlung:我知道这一点及其背后的原理。我只是说这很烦人:)

– Privman附近
2011-2-5在9:27

#15 楼

我的2cents是在两种情况下应避免使用它们:

1)当您有一个在许多行中使用的变量,并且在使用该变量的第一个语句中对其进行增减(或最后一个,甚至更糟的是,在中间):

// It's Java, but applies to Js too
vi = list.get ( ++i );
vi1 = list.get ( i + 1 )
out.println ( "Processing values: " + vi + ", " + vi1 )
if ( i < list.size () - 1 ) ...


在这样的示例中,您很容易错过变量是自动递增/递减的,甚至删除了第一个声明。换句话说,仅在非常短的块中使用变量,或者仅在几个close语句中变量出现在块中的地方使用它。

2)如果有多个++和-关于同一语句中的同一变量。很难记住在这种情况下会发生什么情况:

result = ( ++x - --x ) * x++;


考试和专业考试都询问上述示例,实际上我在寻找时偶然发现了这个问题关于其中一个的文档,但是在现实生活中,不应强迫人们对一行代码进行太多思考。

#16 楼

Fortran是类似于C的语言吗?它既没有++也没有-。这是编写循环的方式:

     integer i, n, sum

      sum = 0
      do 10 i = 1, n
         sum = sum + i
         write(*,*) 'i =', i
         write(*,*) 'sum =', sum
  10  continue


每次循环时,索引元素i都会由语言规则递增。如果要增加1以外的值,例如向后数加2,则语法为...

      integer i

      do 20 i = 10, 1, -2
         write(*,*) 'i =', i
  20  continue


Python C一样吗?它使用范围和列表理解以及其他语法来绕开增加索引的需求:

print range(10,1,-2) # prints [10,8.6.4.2]
[x*x for x in range(1,10)] # returns [1,4,9,16 ... ]


因此,基于对两种替代方案的初步探索,语言设计人员可以避免++和-通过预期用例并提供备用语法。

与具有++和-的过程语言相比,Fortran和Python是否明显更不吸引人。我没有证据。

我声称Fortran和Python类似于C,因为我从未遇到过流利的C语言,他们不能以90%的准确度正确猜出没有混淆的Fortran或Python的意图。 。

评论


是的,IFortran是1950年代的,而C是1970年代的,所以对于Fortran而言,可能不像C。 Python缺少诸如plusplus运算符之类的示例很有趣。在Python中更容易处理数据结构的迭代,Python是“不同的”面向对象的JavaScript。也许Crockford的建议是关于使用更多类似于OO的语法进行迭代。

– artlung
2009年6月9日在17:34