Programming

JavaScript에서 증가 (“++”) 및 감소 (“-”) 연산자를 피해야하는 이유는 무엇입니까?

procodes 2020. 3. 1. 16:02
반응형

JavaScript에서 증가 (“++”) 및 감소 (“-”) 연산자를 피해야하는 이유는 무엇입니까?


jslint 도구 중 하나 는 다음과 같습니다.

++ 및-
++ (증가) 및-(감소) 연산자는 과도한 까다 로움을 유발하여 잘못된 코드에 기여하는 것으로 알려져 있습니다. 바이러스 및 기타 보안 위협을 가능하게하는 잘못된 아키텍처에 이어 두 번째입니다. 이 연산자의 사용을 금지하는 플러스 플러스 옵션이 있습니다.

PHP와 같은 구문 $foo[$bar++]은 쉽게 하나씩 오류가 발생할 수 있지만 a while( a < 10 ) do { /* foo */ a++; }또는 보다 루프를 제어하는 ​​더 좋은 방법을 알 수는 없습니다 for (var i=0; i<10; i++) { /* foo */ }.

" ++"및 " --"구문 이 부족 하거나 다르게 처리하는 유사한 언어가 있거나, 누락 된 " ++"및 " --" 를 피할 수있는 다른 근거가 있기 때문에 jslint가 강조 표시 합니까?


내 견해는 항상 ++를 사용하는 것입니다.

i++;
array[i] = foo;

대신에

array[++i] = foo;

그 이상은 프로그래머에게 혼란을 줄 수 있으며 내 견해로는 가치가 없습니다. 증분 연산자의 사용은 관용적이므로 항상 명확하므로 For 루프는 예외입니다.


나는 그 조언에 솔직히 혼란스러워합니다. 저의 일부는 자바 스크립트 코더에 대한 경험 부족 (인지 또는 실제)과 관련이 있는지 궁금합니다.

누군가가 일부 샘플 코드에서 "해킹"하는 방법이 ++와 무고한 실수를 할 수있는 방법을 알 수 있지만 숙련 된 전문가가 왜 피할 수 있는지 알 수 없습니다.


C에는 다음과 같은 일을 한 역사가 있습니다.

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

문자열을 복사하려면 아마도 이것이 언급 한 과도한 속임수의 원천 일 것입니다.

그리고 무엇에 대한 질문이 항상 있습니다

++i = i++;

또는

i = i++ + ++i;

실제로 그렇습니다. 일부 언어로 정의되어 있으며 다른 언어에서는 어떤 일이 일어날 지 보장하지 않습니다.

이러한 예를 제외하고는 ++증가 하는 데 사용하는 for 루프보다 관용적 인 것이 없다고 생각 합니다. 어떤 경우에는 foreach 루프 또는 다른 조건을 확인한 while 루프를 사용하여 벗어날 수 있습니다. 그러나 증분을 사용하지 않고 코드를 왜곡하는 것은 어리 석습니다.


JavaScript The Good Parts를 읽으면 for 루프 에서 Crockford의 i ++ 대체 가 i + = 1 (i = i + 1 아님) 임을 알 수 있습니다 . 그것은 꽤 깨끗하고 읽을 수 있으며 무언가 까다로운 것으로 변형 될 가능성이 적습니다.

크록 포드는 자동 증가를 허용하지하고 자동 감소 만든 옵션 jsLint에 있습니다. 조언을 따를 지 여부를 선택합니다.

내 자신의 개인 규칙은 자동 증가 또는 자동 감소와 결합 된 것을하지 않는 것입니다.

수년간의 C 경험을 통해 간단한 사용으로 버퍼 오버런 (또는 배열 인덱스가 범위를 벗어남)을 얻지 못한다는 것을 배웠습니다. 그러나 같은 문장에서 다른 일을하는 "과도하게 까다로운"관행에 빠지면 버퍼 오버런이 발생한다는 것을 알게되었습니다.

따라서 내 규칙의 경우 for 루프 에서 증가분으로 i ++를 사용하는 것이 좋습니다.


루프에서는 무해하지만 할당 문에서 예기치 않은 결과가 발생할 수 있습니다.

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 대 1 오류를 생성 할 수 있습니다. 다행히도, 그들은 또한 불필요합니다. 변수에 1을 더하는 더 좋은 방법이 있습니다.

참고 문헌


다음 코드를 고려하십시오

    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 루프가 ++에서는 괜찮다고 생각하지만 같은 줄이나 동일한 명령어에 여러 ++ 기호가있을 때 조심하십시오.


증가 및 감소 연산자의 "사전"및 "사후"특성은 익숙하지 않은 사용자에게는 혼동을 줄 수 있습니다. 그것이 까다로울 수있는 한 가지 방법입니다.


제 생각에는 "명시적인 것이 항상 암시적인 것보다 낫습니다." 어느 시점에서이 increments 문과 혼동 될 수 있습니다 y+ = x++ + ++y. 좋은 프로그래머는 항상 자신의 코드를 더 읽기 쉽게 만듭니다.


나는 이것에 대해 Douglas Crockford의 비디오를보고 있었고 증가 및 감소를 사용하지 않는 것에 대한 그의 설명은

  1. 과거에는 다른 언어로 배열의 경계를 허물고 모든 방식의 나쁨과
  2. 더 혼란스럽고 경험이 부족한 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를 알고있는 팀이 있다면 걱정할 필요가 없습니다. 코드를 읽는 사람을 기억해야한다고 생각합니다. 그렇지 않은 경우 의견을 말하고 다르게 작성하십시오.해야 할 일을하십시오. 나는 증가와 감소가 본질적으로 나쁘거나 버그 생성, 또는 취약성 생성이라고 생각하지 않습니다. 독자에 따라 읽기 어려울 것입니다.

Btw, 나는 Douglas Crockford가 어쨌든 전설이라고 생각하지만, 그는 가치가없는 운영자에 대해 많은 두려움을 일으켰습니다.

그래도 틀린 것으로 판명되었습니다 ...


++ 또는-를 피하는 가장 중요한 근거는 연산자가 값을 반환하고 동시에 부작용을 일으켜 코드에 대한 추론을 어렵게한다는 것입니다.

효율성을 위해 다음을 선호합니다.

  • ++ i 반환 값을 사용하지 않을(임시 없음)
  • 반환 값을 사용할i ++ (파이프 라인 중단 없음)

저는 Crockford 씨의 팬이지만이 경우에는 동의하지 않습니다. ++i구문 분석 할 텍스트가 25 % 적 i+=1 으며 분명히 더 명확합니다.


또 다른 예는 증분 값의 간단한 반환으로 다른 것보다 더 간단합니다.

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 문에서 증가 후 / 감소를 사용해서는 안됩니다. 그러나 리턴은 후행 증가 / 감소 연산자를 "잡지"않습니다.

function closureIncrementTest() {
    var x = 0;

    function postIncrementX() {
        return x++;
    }

    var y = postIncrementX();

    console.log(x); // 1
}

프로그래머가 사용하는 언어에 능숙해야한다고 생각합니다. 명확하게 사용하십시오. 잘 사용하십시오. 나는 그들이 사용하는 언어를 인위적으로 약화시켜야한다고 생각 하지 않습니다 . 나는 경험에서 말합니다. 나는 한때 말 그대로 코볼 상점 옆에서 일했는데 '너무 복잡했기 때문에'ELSE를 사용하지 않았습니다. Reductio ad absurdam.


이것이 그의 추론의 일부인지는 모르겠지만 잘못 작성된 축소 프로그램을 사용하면 x++ + y바뀔 수 있습니다 x+++y. 그러나 잘못 작성된 도구는 모든 종류의 혼란을 초래할 수 있습니다.


기존 답변 중 일부에서 언급했듯이 (귀찮게 언급 할 수없는) 문제는 x ++ ++ x가 다른 값으로 평가되기 때문에 (증가 전과 비교 후) 명백하지 않고 매우 혼란 스러울 수 있습니다. 해당 값이 사용되는 경우 cdmckay는 증가 연산자를 사용할 수 있도록 현명하게 제안하지만 리턴 된 값이 사용되지 않는 방식으로 만 (예 : 자체 행). 또한 for 루프 내에 표준 사용을 포함시킬 것입니다 (그러나 반환 값이 사용되지 않는 세 번째 문에서만). 다른 예를 생각할 수 없습니다. 나 자신을 "번트"했으므로 다른 언어에 대해서도 같은 지침을 권장합니다.

이 과도한 엄격은 많은 JS 프로그래머가 경험이 없기 때문에 발생한다는 주장에 동의하지 않습니다. 이것은 "과도한"프로그래머의 전형적인 글쓰기의 정확한 종류이며, 더 전통적인 언어와 그러한 언어의 배경을 가진 JS 개발자에게 훨씬 더 일반적이라고 확신합니다.


내 경험상, ++ i 또는 i ++는 연산자의 작동 방식에 대해 처음 배우는 것 외에 다른 혼란을 일으키지 않았습니다. 가장 기본적인 for 루프와 while 루프는 운영자가 사용할 수있는 언어로 가르치는 고등학교 또는 대학 과정에서 가르치는 것입니다. 개인적으로 ++가 별도의 줄에있는 것보다 더 잘 보이고 읽기 위해 아래의 것과 같은 것을하는 것을 발견했습니다.

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

결국 그것은 스타일 선호이며 더 이상은 아닙니다. 더 중요한 것은 코드 에서이 작업을 수행 할 때 일관성을 유지하여 동일한 코드로 작업하는 다른 사람들이 다른 방식으로 동일한 기능을 수행하고 처리 할 필요가 없도록하는 것입니다 .

또한 Crockford는 i- = 1을 사용하는 것 같습니다.이 값은 --i 또는 i--보다 읽기 어렵습니다.


내 2 센트는 두 가지 경우에 피해야한다는 것입니다.

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 ) ...

이와 같은 예제에서 변수가 자동 증가 / 감소되거나 첫 번째 명령문을 제거하는 것을 쉽게 놓칠 수 있습니다. 다시 말해, 아주 짧은 블록에서만 또는 변수가 블록에서 두 개의 가까운 명령문에서만 나타나는 곳에만 사용하십시오.

2) 여러 ++ 및-의 경우 동일한 명령문에서 동일한 변수에 대해. 다음과 같은 경우에 어떤 일이 발생하는지 기억하기가 매우 어렵습니다.

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

시험과 전문 테스트는 위와 같은 예제를 요구하며 실제로 그중 하나에 대한 문서를 찾는 동안이 질문에 걸려 들었습니다. 그러나 실제로는 한 줄의 코드에 대해 그렇게 많이 생각해서는 안됩니다.


포트란은 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

파이썬 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의 의도를 정확하게 추측 할 수 없습니다.

참고 URL : https://stackoverflow.com/questions/971312/why-avoid-increment-and-decrement-operators-in-javascript



반응형