Зачем избегать приращений ( "++" ) и декрементов ( "-" ) в JavaScript?
Один из советов для инструмента jslint:
++ и -
++ (приращение) и - (декремент) операторы, как известно, вносят свой вклад в поощряя чрезмерную хитрость. Oни уступают только ошибочной архитектуре в обеспечении вирусов и других угрозы безопасности. Есть плюс вариант, запрещающий использование этих операторы.
Я знаю, что PHP-конструкции, такие как $foo[$bar++]
, могут легко возникать с ошибками "один за другим", но я не мог понять, как лучше управлять контуром, чем while( a < 10 ) do { /* foo */ a++; }
или for (var i=0; i<10; i++) { /* foo */ }
.
Является ли jslint подсветкой их, потому что есть некоторые аналогичные языки, которые не имеют синтаксиса "++
" и "--
" или обрабатывают его по-другому или существуют другие соображения для избежания "++
" и "--
", чтобы я мог отсутствовать?
Ответы
Ответ 1
На мой взгляд, всегда использовать ++ и - сами по себе в одной строке, как в:
i++;
array[i] = foo;
вместо
array[++i] = foo;
Все, что за этим, может запутать некоторых программистов, и, на мой взгляд, оно того не стоит. Циклы for являются исключением, так как использование оператора приращения идиоматично и, следовательно, всегда понятно.
Ответ 2
Я откровенно смущен этим советом. Часть меня удивляется, если она имеет больше общего с отсутствием опыта (воспринимаемого или фактического) с javascript-кодонами.
Я могу видеть, как кто-то просто "взломал" в некотором примерном коде, мог совершить невинную ошибку c++ и -, но я не понимаю, почему опытный профессионал избегал бы их.
Ответ 3
В C существует история с такими вещами, как:
while (*a++ = *b++);
чтобы скопировать строку, возможно, это источник чрезмерного обмана, на который он ссылается.
И всегда есть вопрос о том, что
++i = i++;
или
i = i++ + ++i;
на самом деле делаю. Он определен на некоторых языках, а в других нет гарантии того, что произойдет.
В приведенных примерах я не думаю, что есть что-то более идиоматическое, чем цикл for, который использует ++
для увеличения. В некоторых случаях вы можете уйти с помощью цикла foreach или цикла while, который проверял другое состояние. Но искажение вашего кода, чтобы попытаться избежать приращения, смешно.
Ответ 4
Если вы прочитали JavaScript "Хорошие детали", вы увидите, что замена Crockford для я ++ в цикле для равна я + = 1 (не я = я + 1). Это довольно чистый и читаемый, и с меньшей вероятностью превратится во что-то "сложное".
Крокфорд запретил автоинкремент и автоопределение в jsLint. Вы выбираете, следует ли следовать совету или нет.
Мое личное правило состоит в том, чтобы не делать ничего в сочетании с автоинкрементами или автоопределением.
Я узнал из многолетнего опыта работы в C, что я не получаю переполнения буфера (или индекс массива за пределами границ), если я буду продолжать использовать его просто. Но я обнаружил, что я получаю переполнение буфера, если я попадаю в "чрезмерно сложную" практику выполнения других вещей в том же самом заявлении.
Итак, для моих собственных правил использование я ++ в качестве приращения цикла для прекрасное.
Ответ 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
Путаница между преинкрементом и постинкрементом может привести к ошибкам, которые чрезвычайно трудно диагностировать. К счастью, они также являются полными ненужными. Есть лучшие способы добавить 1 к переменной.
Рекомендации
Ответ 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++;
так как я ++ получает оценку дважды, выход
(от отладчика 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;
Обратите внимание, что вывод одинаков. Теперь вы можете подумать, что ++ я и я ++ одинаковы. Они не
[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
Таким образом, они не совпадают, смешивание обоих приводит к не столь интуитивному поведению. Я думаю, что для циклов в порядке c++, но следите, когда у вас есть несколько символов ++ в одной строке или той же инструкции
Ответ 7
"pre" и "post" характер операторов приращения и декремента могут быть запутанными для тех, кто не знаком с ними; что один из способов, которым они могут быть сложными.
Ответ 8
На мой взгляд, "Явный всегда лучше, чем неявный".. В какой-то момент вас может смутить этот оператор increments y+ = x++ + ++y
. Хороший программист всегда делает свой код более удобочитаемым.
Ответ 9
Я смотрел видео Douglas Crockford по этому поводу, и его объяснение невозможности использования приращений и декретов состояло в том, что
- Он использовался в прошлом на других языках, чтобы сломать границы массивов и вызвать все манеры плохости и
- Что более запутанные и неопытные разработчики JS не знают точно, что он делает.
Во-первых, массивы в JavaScript имеют динамический размер и поэтому, простите меня, если я ошибаюсь, невозможно разорвать границы массива и получить доступ к данным, к которым не следует обращаться с помощью этого метода в JavaScript.
Во-вторых, следует ли нам избегать сложных вещей, конечно, проблема заключается не в том, что у нас есть это средство, но проблема в том, что есть разработчики, которые утверждают, что делают JavaScript, но не знают, как работают эти операторы? Это достаточно просто. значение ++, дайте мне текущее значение и после выражения добавьте его к нему, значение ++, увеличьте значение, прежде чем дать мне это.
Выражения, такие как ++ + ++ 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 изнутри, тогда вам не нужно беспокоиться. Если нет, то прокомментируйте это, напишите его по-другому и т.д. Сделайте то, что вам нужно сделать. Я не думаю, что прирост и декремент по своей сути являются плохими или генерируют ошибки или создают уязвимость, возможно, менее читабельны в зависимости от вашей аудитории.
Кстати, я думаю, что Дуглас Крокфорд в любом случае легенда, но я думаю, что он напугал оператора, который этого не заслужил.
Я живу, чтобы быть ошибочным, хотя...
Ответ 10
Самое важное обоснование для избежания ++ или - заключается в том, что операторы возвращают значения и вызывают побочные эффекты одновременно, что затрудняет рассуждение о коде.
Для эффективности я предпочитаю:
- ++ i, когда не использует возвращаемое значение (без временных)
- я ++, когда использует возвращаемое значение (без конвейера)
Я поклонник мистера Крокфорда, но в этом случае я должен не согласиться. ++i
на 25% меньше текста для разбора, чем i+=1
и, возможно, более ясного.
Ответ 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 не должно использоваться пост-инкремент/декремент, если вы хотите, чтобы этот оператор влиял на результат. Но возврат не "улавливает" операторы post-increment/decment:
function closureIncrementTest() {
var x = 0;
function postIncrementX() {
return x++;
}
var y = postIncrementX();
console.log(x); // 1
}
Ответ 12
Я думаю, что программисты должны быть компетентными на языке, который они используют; используйте его четко; и использовать его хорошо. Я не думаю, что они должны искусственно калечить язык, который они используют. Я говорю по опыту. Я когда-то работал буквально по соседству с магазином Cobol, где они не использовали ELSE, потому что это было слишком сложно. Reductio ad absurdam.
Ответ 13
Я не знаю, было ли это частью его рассуждений, но если вы используете плохо написанную программу минерализации, она может превратить x++ + y
в x+++y
. Но опять же, плохо написанный инструмент может нанести все виды хаоса.
Ответ 14
Как уже упоминалось в некоторых из существующих ответов (которые досадно, что я не могу комментировать), проблема в том, что x ++ ++ x оценивает разные значения (до vs после инкремента), что не очевидно и может быть очень запутанно - , если используется это значение. cdmckay предлагает разумно разрешить использование оператора инкремента, но только так, чтобы возвращаемое значение не использовалось, например. по собственной линии. Я бы также включил стандартное использование в цикл for (но только в третьем операторе, возвращающее значение которого не используется). Я не могу придумать другой пример. Будучи сам "сожжен", я рекомендовал бы то же самое руководство для других языков.
Я не согласен с утверждением, что эта чрезмерность объясняется тем, что многие программисты JS неопытны. Это точный тип письма, типичный для "слишком умных" программистов, и я уверен, что он гораздо более распространен на более традиционных языках и с разработчиками JS, у которых есть фон на таких языках.
Ответ 15
По моему опыту, ++ я или я ++ никогда не вызывали путаницы, кроме того, когда вы впервые узнали о том, как работает оператор. Это важно для большинства базовых циклов и циклов, которые учат любым школьным или колледжским курсам, преподаваемым на языках, где вы можете использовать оператора. Я лично нахожу, что делаю что-то вроде того, что ниже, чтобы выглядеть и читать лучше, чем что-либо c++, находящимся в отдельной строке.
while ( a < 10 ){
array[a++] = val
}
Ответ 16
Мои 2центы в том, что их следует избегать в двух случаях:
1) Когда у вас есть переменная, которая используется во многих строках, и вы увеличиваете/уменьшаете ее в первом утверждении, которое ее использует (или последнее или, что еще хуже, посередине):
// It 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 ) ...
В таких примерах вы можете легко пропустить, что переменная автоматически увеличивается или уменьшается или даже удаляет первый оператор. Другими словами, используйте его только в очень коротких блоках или где переменная появляется в блоке только на пару закрытых операторов.
2) В случае множественного ++ и - о той же переменной в том же самом выражении. Очень сложно запомнить, что происходит в таких случаях:
result = ( ++x - --x ) * x++;
Экзамены и профессиональные тесты спрашивают о таких примерах, как выше, и действительно я наткнулся на этот вопрос, ища документацию об одном из них, но в реальной жизни не следует заставлять так много думать о одной строке кода.
Ответ 17
Является ли Fortran C-подобным языком? У него нет ни ++, ни.... Вот как вы пишете цикл:
integer i, n, sum
sum = 0
do 10 i = 1, n
sum = sum + i
write(*,*) 'i =', i
write(*,*) 'sum =', sum
10 continue
Индексный элемент я каждый раз увеличивается с помощью языковых правил через цикл. Если вы хотите увеличивать значение на что-то, отличное от 1, считайте, например, два раза, синтаксис...
integer i
do 20 i = 10, 1, -2
write(*,*) 'i =', i
20 continue
Является ли Python C-like? Он использует методы определения диапазонов и списков и другие синтаксисы, чтобы обойти необходимость увеличения индекса:
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.