본문 바로가기
자바스크립트

[JavaScript공부 - 8] Operators(연산자)

by 곰돌찌 2019. 1. 30.

초등학교 때부터 수학을 배우면서 다양한 연산자를 접할 수 있었음. 연산자에는 더하기(+), 빼기(-), 곱하기(*) 등과 같은 것들이 있음.


이번 포스팅에서는 학교 수학시간에 다루지 않았던 연산자의 면에 집중해서 해볼 예정.



용어 : "unary(단항)", "binary(이항)", "operand(피연산자)"



일단 연산자에 들어가기 전에 용어에 대해 정의를 하고 넘어갈 예정.


◉ operand(피연산자) : 피연산자는 연산을 하는 대상을 의미함. 예를 들어 5 * 2 와 같은 연산을 할 때, 2개의 피연산자가 존재함. 이 연산에서 왼쪽 피연산자는 5가 될 것이고 오른쪽 피연산자는 2가 될 것임. 가끔 사람들은 피연산자 대신에 argument라고 부름


◉ 연산이 unary(단항)이라는 의미는 피연산자가 하나일 때를 의미함. 예를들어 음수를 표시하는 - 가 대표적인 예임.


1
2
3
4
5
let x = 1;
 
= -x;
 
alert(x); // -1, unary negation was applied
cs


◉ 연산이 binary(이항)이라는 의미는 피연산자가 두 개라는 의미. 이항에서의 - 는 두 피연산자를 뻬는 역할을 수행함.


1
2
3
4
let x = 1;
let y = 3;
 
alert(y - x); //2, binary minus subtracts values
cs

위에 보는 바와 같이 - 와 같은 경우에는 2개의 다른 연산 역할을 하고 있음 : 단항에서는 음수를 만들어주는 역할, 이항에서는 두 피연산자의 뺄셈을 해주는 역할을 함.


String 연결을 위한 이항연산 +


이제 학교에서 배운 수학의 연산과는 다른 자바스크립트의 연산에 대해서 알아보자!

보통, 더하기(+)연산자는 숫자를 더할 때 사용함.

그러나 + 연산자가 String 타입의 값에 적용되었을 때, + 연산자는 그 String 타입의 값을 합치는 역할을 함.

1
2
let s = "my" + "string";
alert(s); // mystring
cs

만약 피연산자 중 하나가 String이라면 다른 피연산자는 String값으로 자동 변환됨.

1
2
alert("1" + 2); // "12"
alert(2 + "1"); // "21"
cs

위에 보는 바와 같이 첫 번째 피연산자가 String타입이던, 두 번째 피연산자가 String타입이던 그 부분은 상관이 없음. 규칙은 간단함! 둘 중 하나의 피연산자가 String 타입이라면 다른 하나는 String타입으로 자동 변환됨.

그러나 이 연산자는 왼쪽에서 부터 오른쪽으로 연산되기 때문에 String 타입 앞에 두개 의 수가 있다면 앞의 두 수는 숫자로 더해진 다음 String으로 변환됨.

1
alert(2 + 2 + '1'); // "41", not "221"
cs

String 연결과 변환은 이항 연산 더하기(+)의 특별한 특징임. 다른 수학 연산은 숫자 타입만 적용되며 모든 피연산자들을 숫자로 바꿔 버림.

예를들어 뺄셈과 나눗셈은 String타입의 값을 number로 바꾸어 버림.

1
2
alert(2 - '1'); // 1
alert('6' / '2'); // 3
cs


숫자로의 변환, 단항 연산 +


더하기(+)는 두 가지 형태로 존재함. 첫 번째로, 위에서 언급했던 이항 연산 + , 두 번째로, 단항 연산의 형태가 있음.


즉, 단항 연산 +는 더하기 연산 +를 하나의 값에 적용시키고 숫자로 어떠한 연산을 하지 않을 때 적용됨. 그러나 피연산자가 숫자가 아닐 때, 단항 연산 +는 값을 숫자로 바꾸어 버림.


1
2
3
4
5
6
7
8
9
10
//No effect on numbers
let x = 1;
alert(+x); //1
 
let y = -2;
alert(+y); // -2
 
//Converts non-numbers into a number
alert(+true); // 1
alert(+""); // 0
cs


위의 예를 보면 Number(..) 함수로 형변환하는 역할과 비슷한 역할을 함. 단지 Number(..) 함수보다 더 짧게 쓰는 차이만 있음.


종종 String을 숫자로 바꾸어야 하는 경우가 있음. 예를 들어 HTML의 form 필드에서 값을 가져올 때, 그 값들이 대부분 String타입임.


만약 얘네를 더하기 연산을 하면 위에서 언급한 바와 같이 String타입이기 때문에 값이 더해지지 않고 연결됨.


1
2
3
4
let apples = '2';
let oranges = '3';
 
alert(apples + oranges); // 23
cs

만약 이 값들을 숫자로 취급하고 싶으면 + 연산을 앞에 넣어 값을 더할 수 있도록 할 수 있음.

1
2
3
4
let apples = '2';
let oranges = '3';
 
alert(+apples + +oranges); // 5
cs

위에 예시를 수학자의 관점에서 본다면 + 기호를 매우 많이 써서 이상해보일지 모름. 그러나 프로그래머 관점에서 보면 이상할 것이 없는 코드임! 단항의 + 가 먼저 적용되어 변수나 값을 String타입에서 number타입으로 형변환을 해주고 그 다음 이항 연산의 덧셈(+)을 해줌!



연산의 우선순위



만약 코드에서 하나 이상의 연산을 한다면 실행의 순서는 연산의 우선순위에 의해 결정됨. 즉, 연산자의 보이지 않는 우선순위가 있다는 말!


학교에서는 1 + 2 * 2가 있다면 곱셈을 먼저한 다음 덧셈을 하라는 방식으로 배움. 이게 바로 연산의 우선순위임! 곱셈은 덧셈보다 연산 우선순위가 높다고 할 수 있음.


어떠한 연산을 둘러싸고 있는 괄호는 이런 연산의 우선순위를 무시하고 먼저 계산됨, 따라서 1 + 2 * 2와 (1 + 2) * 2의 계산의 값은 달라지게 됨.


자바스크립트에는 다양한 연산자가 존재하며 각 연산자는 우선순위가 부여되어 있기 때문에 우선순위가 높은 연산은 먼저 실행됨. 만약 우선 순위가 같은 레벨이라면 실행의 순서는 왼쪽에서 부터 오른쪽으로 가는 것이 일반적임.


아래는 기억할만한 우선순위 테이블임.

PrecedenceNameSign
16unary plus+
16unary negation-
14multiplication*
14division/
13addition+
13subtraction-
3assignment=


위에 보는 바와 같이 단항 플러스의 우선순위는 16이고, 이항 연산의 덧셈은 13임. 따라서 "+apples + +orages"의 식에서 단항 플러스가 먼저 실행되게 됨!



Assignment(대입)



대입 연산(=)도 연산자의 종류 중 하나임. 우선 순위 테이블에서 보면 대입연산의 우선순위는 3으로 매우 낮은 쪽에 속해 있음.


그래서 x = 2 * 2 + 1과 같은 연산을 할 때 뒤의 계산이 다 된 후,  x라는 변수에 결과 값이 대입됨.


1
2
3
let x = 2 * 2 + 1;
 
alert(x); // 5
cs


대입은 여러 변수를 한번에 체인 처럼 연결하여 대입 연산도 가능함.


1
2
3
4
5
6
7
let a, b, c;
 
= b = c = 2 + 2;
 
alert(a); // 4
alert(b); // 4
alert(c); // 4
cs


체인 대입은 오른쪽에서 왼쪽으로 실행됨. 첫 번째로 가장 오른쪽에 있는 표현식인 2 + 2는 먼저 값인 4가 계산되고 그 결과 값이 c, b, a 순으로 대입됨.


# 대입 연산 '='는 값을 리턴함.


연산자는 항상 값을 리턴하게 되어있음. 이는 덧셈이나 곱셈이 값을 연산해서 결과의 값을 리턴해줌! 이 규칙은 대입연산에서도 적용됨.


x = value라는 함수는 쓴다고 가정하면 value는 변수 x에 담겨서 그 값을 리턴함.


1
2
3
4
5
6
7
let a = 1;
let b = 2;
 
let c = 3 - (a = b + 1);
 
alert(a); // 3
alert(c); // 0
cs

위의 예제에서 보면 (a = b + 1)의 결과값인 3이 a에 담겨서 리턴됨. 그 다음 c에 할당되기 위해서 앞이 3이라는 숫자와 뺄셈이 실행됨.

뭔가 신기해보이는 코드일 수 있음! 평소에는 그럴일은 없지만 가끔 볼 수도 있는 코드이기 때문에 어떻게 작동하는지는 알아두는 것이 좋음. 그리고 코드를 짤 때 이런 방식으로 짜는 것은 추천하지 않음!이런 코드는 읽기도 어렵고 깔끔하지 않기 때문!



나머지 연산 %



나머지 연산인 %는 보이는 것처럼 퍼센트 연산과 관련있지는 않음..!


a % b의 결과는 a를 b로 나누었을 때의 나머지 정수를 반환함.


1
2
3
alert(5 % 2); // 1
alert(8 % 3); // 2
alert(6 % 3); // 0
cs



지수 연산 **



지수 연산 **는 최근에 자바스크립트에 추가된 연산자임.


a ** b의 결과는 지수 계산처럼 a라는 값을 b번 곱하는 결과가 나타남.


1
2
3
alert(2 ** 2); // 4 (2 * 2)
alert(2 ** 3); // 8 (2 * 2 * 2)
alert(2 ** 4); // 16 (2 * 2 * 2 * 2)
cs

이런 지수 연산은 a나 b가 정수가 아닐 때도 일어남!

1
2
alert4 ** (1/2) ); // 2 (same as square root (제곱근))
alert8 ** (1/3) ); // 2 (same as cubic root(세제곱근))
cs



증가/감소 연산


숫자가 1씩 증가하거나 감소하는 것은 매우 흔한일임. 그래서 자바스크립트에는 이런 연산을 위한 특별한 연산자가 있음!



- 1 증가 연산 : ++

1
2
3
let counter = 2;
counter++;
alert(counter); // 3
cs


- 1 감소 연산 : --

1
2
3
let counter = 2;
counter--;
alert(counter); // 1
cs

# 주의 하기!

1 증가/감소 연산은 오직 변수에만 적용함. 5++ 처럼 숫자에 직접 증가/감소 연산을 넣을 경우 에러가 발생함


증가/감소 연산인 ++와 --는 변수의 앞이나 뒤에 붙을 수 있음.


- 연산자가 변수 뒤에 오는 경우 "후치 형태(postfix form)"이라고 부름 ex) counter++

- 연산자가 변수 앞에 오는 경우 "전치 형태(prefix form)"이라고 부름 ex) ++counter


두 형태 모두 counter라는 변수의 값을 1 증가 시키는 역할을 하는 것은 동일함.


그렇다면 두 형태의 차이는 무엇일까? 앞서 언급한 바와 같이 모든 연산자는 값을 리턴함. 증가/감소 연산도 예외가 아님. 후치 형태와 전치 형태의 차이는 후치 형태(postfix form)은 증가/감소 이전의 값을 리턴하는 반면에 전치 형태(prefix form)은 증가/감소 이후의 값을 리턴함.


아래의 예제를 보자!


1
2
3
4
5
6
7
8
9
10
11
12
13
let counter = 1;
 
let a = ++counter;
 
alert(a); // 2
 
////////////////////
 
let counter = 1;
 
let a = counter++;
 
alert(a); // 1
cs

위의 전치 형태(prefix form)은 새로운 값을 리턴하기 때문에 alert에서 2라는 메세지를 보여주며, 아래는 후치 형태(postfix form)은 이전 값을 이전의 값을 리턴하기 때문에 alert에서 1이라는 메세지를 보여주게 됨.


# 다른 연산자 사이에서의 증가/감소 연산


연산자 ++/--는 다른 연산자가 사용되는 코드 안에서 함께 쓰일 수 있음. 증가/감소 연산은 다른 수학 연산자들보다 우선순위가 높기 때문에 먼저 실행됨.


1
2
let counter = 1;
alert(2 * ++counter); //4
cs

1
2
3
let counter = 1;
alert(2 * counter++); //2
alert(counter); //2
cs

물론 이런 코드는 읽기 어렵게 만들 수 있음. 한 줄에 여러 연산자가 있는 건 딱히 좋지 않음.

따라서 한 줄에 하나의 수행을 할 수 있도록 아래와 같이 코드를 해주는 게 좋음.

1
2
3
let counter = 1;
alert(2 * counter);
counter++;
cs



비트 연산자



비트연산자는 32비트의 정수를 다루며 이진 표현의 레벨에서 이루어짐.


이 연산자들은 JavaScript에서만 쓰이는 것이 아니고 대부분의 프로그래밍 언어에서 사용됨.


이 연산자들에는 AND(&), OR(|), XOR(^), NOT(~), LEFT SHIFT(<<), RIGHT SHIFT(>>), ZERO-FILL RIGHT SHIFT(>>>) 가 있음.


이런 연산자들은 거의 사용을 할 일이 없기 때문에 이런 연산자들이 있다는 것만 언급하고 필요하다면 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators 이 사이트를 참조하면 될 듯!



한 위치에서 수정하기



가끔 코드를 짤 때, 한 변수의 값에 추가 연산을 한 다음 같은 변수에 이 결과 값을 저장해야할 때가 있음.


아래와 같은 예시가 그런 경우임!


1
2
3
let n = 2;
= n + 5;
= n * 2;
cs

이런 경우에는 += 나 *=를 사용하여 짧게 줄일 수 있음.

이런 연산자는 다른 연산들을 한 후에 실행됨.

1
2
3
4
let n = 2;
*= 3 + 5;
 
alert(n); // 16 ( n = 2 * (3 + 5) )

cs



콤마 (Comma)


콤마( , ) 연산자는 연산자 중에서 가장 사용하지 않는 코드 중 하나임. 가끔 짧은 코드를 작성하기 위해서 사용하기 때문에 이 코드가 어떤 방식으로 동작하는지는 알 필요가 있음.


콤마 연산자는 몇몇 표현을 콤마로 나누어서 판단할 수 있도록함. 콤마로 분리하여 각각 연산을 하지만 결과 값은 결국 마지막만 리턴됨.


1
2
let a = (1 + 23 + 4);
alert(a); // 7 (the result of 3 + 4)
cs


위의 예시에서 보면 1+2와 3+4의 연산은 각각 이루어 지지만 가장 마지막 값은 7만 메세지에 나타남.


# 콤마는 가장 낮은 우선순위를 갖고 있습니다!


콤마는 할당 연산자(=)보다도 낮은 우선순위를 가지고 있음. 그래서 콤마 연산자를 사용하기 위해서는 괄호로 묶어주는 것이 필요함!


가끔 다른 사람의 코드를 보면 한 줄에 여러 기능을 할 수 있도록 복잡한 구조로 만들 때 사용함/

1
2
3
4
// threee operations in one line
for (a = 1, b = 3, c = a * b; a < 10; a++) {
    ....
}
cs

이런 방식은 자바스크립트 프레임워크에서 많이 사용되는 것을 볼 수 있음. 그래서 어떤 방식으로 동작하는 지 알기 위해 콤마 연산자를 언급함. 그러나 보통은 코드의 가독성을 위해서 콤마 연산자를 사용할지 안할지는 잘 생각해 보아야 할 필요가 있음!

댓글