Зачем избегать операций увеличения ("++") и уменьшения ("-") в JavaScript? - PullRequest
343 голосов
/ 09 июня 2009

Один из советов для инструмента jslint :

++ и -
++ (увеличение) и - (уменьшение) Операторы, как известно, способствуют плохому коду поощряя чрезмерную хитрость. Oни уступают только неисправной архитектуре в активации вирусов и других угрозы безопасности. Есть плюс вариант, который запрещает использование этих операторы.

Я знаю, что PHP-конструкции, подобные $foo[$bar++], могут легко привести к ошибкам, не связанным с одной, но я не мог найти лучший способ управления циклом, чем while( a < 10 ) do { /* foo */ a++; } или for (var i=0; i<10; i++) { /* foo */ }.

Подсвечивает ли jslint их, потому что есть некоторые похожие языки, в которых нет синтаксиса "++" и "--", или они обрабатываются по-разному, или есть другие причины для избежания "++" и "-- "что я могу пропустить?

Ответы [ 17 ]

388 голосов
/ 09 июня 2009

Я считаю, что всегда использовать ++ и - сами по себе в одной строке, как в:

i++;
array[i] = foo;

вместо

array[++i] = foo;

Все, что за этим, может запутать некоторых программистов, и, на мой взгляд, оно того не стоит. Исключение составляют циклы, поскольку использование оператора приращения идиоматично и, следовательно, всегда понятно.

247 голосов
/ 09 июня 2009

Я откровенно смущен этим советом. Часть меня интересует, связано ли это с отсутствием опыта (воспринимаемого или действительного) с кодировщиками JavaScript.

Я вижу, как кто-то, просто "взломавший" какой-то пример кода, мог совершить невинную ошибку с ++ и -, но я не понимаю, почему опытный профессионал мог бы их избежать.

68 голосов
/ 09 июня 2009

В С есть история таких вещей:

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

чтобы скопировать строку, возможно, это источник чрезмерной хитрости, на которую он ссылается.

И всегда возникает вопрос:

++i = i++;

или

i = i++ + ++i;

на самом деле. Это определено в некоторых языках, а в других нет гарантии, что произойдет.

Помимо этих примеров, я не думаю, что есть что-то более идиоматическое, чем цикл for, который использует ++ для приращения. В некоторых случаях вы можете избежать использования цикла foreach или цикла while, который проверяет другое условие. Но искажать код, пытаясь избежать приращения, смешно.

45 голосов
/ 09 июня 2009

Если вы прочитаете JavaScript The Good Parts, вы увидите, что Крокфорд заменит i ++ в цикле для i + = 1 (не i = i + 1). Он довольно чистый и читаемый, и с меньшей вероятностью превратится во что-то «хитрое».

Крокфорд сделал запрещающий автоинкремент и автоудержание опцией в jsLint. Вы сами решаете, следовать ли совету или нет.

Мое личное правило - не делать ничего в сочетании с автоинкрементом или автообращением.

Я узнал из многолетнего опыта в C, что я не получаю переполнения буфера (или индекса массива за пределами границ), если я буду продолжать использовать его просто. Но я обнаружил, что переполнение буфера действительно происходит, если я впадаю в «чрезмерно хитрую» практику выполнения других вещей в том же выражении.

Так что, для моих собственных правил, использование i ++ в качестве приращения в цикле для хорошо.

25 голосов
/ 08 января 2013

В цикле это безвредно, но в операторе присваивания это может привести к неожиданным результатам:

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 к переменной.

Ссылки

17 голосов
/ 09 июня 2009

Рассмотрим следующий код

    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 подходят для ++, но будьте осторожны, когда у вас есть несколько символов ++ в одной строке или в одной и той же инструкции

16 голосов
/ 26 апреля 2011

На мой взгляд, "Явное всегда лучше, чем неявное." Потому что в какой-то момент вы можете запутаться с этим оператором приращений y+ = x++ + ++y. Хороший программист всегда делает свой код более читабельным.

16 голосов
/ 09 июня 2009

Природа «до» и «после» операторов увеличения и уменьшения может быть запутанной для тех, кто с ними не знаком; это один из способов, которым они могут быть хитрыми.

13 голосов
/ 27 сентября 2010

Я смотрел видео Дугласа Крокфорда по этому поводу, и его объяснение того, как не использовать приращение и уменьшение, состоит в том, что

  1. В прошлом он использовался на других языках, чтобы выходить за рамки массивов и вызывать все виды зла и
  2. То, что JS-разработчики более запутаны и неопытны, точно не знают, что он делает.

Во-первых, массивы в JavaScript имеют динамический размер, и, простите меня, если я ошибаюсь, невозможно разбить границы массива и получить доступ к данным, к которым нельзя обращаться с помощью этого метода в JavaScript.

Во-вторых, должны ли мы избегать сложных вещей, конечно, проблема не в том, что у нас есть эта возможность, а в том, что есть разработчики, которые утверждают, что делают JavaScript, но не знают, как работают эти операторы ?? Это достаточно просто. значение ++, укажите текущее значение и после выражения добавьте к нему единицу, значение ++, увеличьте значение перед тем, как передать его.

Выражения типа a ++ + ++ 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 наизнанку, вам не нужно беспокоиться. Если нет, то прокомментируйте, напишите по-другому и т. Д. Делайте то, что вы должны делать. Я не думаю, что инкремент и декремент по своей сути плох, генерируют ошибки или создают уязвимости, может быть, просто менее читабельны в зависимости от вашей аудитории.

Кстати, я думаю, что Дуглас Крокфорд в любом случае легенда, но я думаю, что он сильно напугал оператора, который этого не заслуживал.

Я живу, чтобы оказаться неправым ...

13 голосов
/ 07 марта 2012

Самым важным обоснованием для отказа от ++ или - является то, что операторы возвращают значения и вызывают побочные эффекты одновременно, что усложняет анализ кода.

Ради эффективности я предпочитаю:

  • ++ i , когда не использует возвращаемое значение (без временного значения)
  • i ++ при с использованием возвращаемого значения (без остановки конвейера)

Я фанат мистера Крокфорда, но в этом случае я должен не согласиться. ++i на 25% меньше текста для разбора, чем i+=1 и , возможно, более четкий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...