출처: https://ko.javascript.info/operators
기본 연산자와 수학
연산자와 피연산자
- 피연산자(operand) 는 연산자가 연산을 수행하는 대상으로, 인수(argument)’라고 불리기도 한다.
- 단항(unary) 연산자: 피연산자를 하나만 받는 연산자 (ex: 부호를 반전하는 마이너스 단항연산자)
- 이항(binary) 연산자 : 두 개의 피연산자를 받는 연산자 (ex: 뺄셈을 하는 마이너스 이항연산자)
- 기호는 같지만 수행하는 연산이 다를 수 있다. 이 경우 피연산자의 개수로 연산자를 구분한다.
거듭제곱 연산자 **
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)
거듭제곱 연산자는 정수가 아닌 숫자에 대해서도 동작한다. 1/2
을 사용하면 제곱근을 구할 수 있다.
1
2
alert( 4 ** (1/2) ); // 2 (1/2 거듭제곱은 제곱근)
alert( 8 ** (1/3) ); // 2 (1/3 거듭제곱은 세제곱근)
이항 연산자 ‘+’와 문자열 연결
이항 연산자 +
의 피연산자로 문자열이 전달되면 덧셈 연산자는 덧셈이 아닌 문자열을 병합(연결)한다.
1
2
let s = "my" + "string";
alert(s); // mystring
따라서 이항 연산자 +
를 사용할 때는 피연산자 중 하나가 문자열이면 다른 하나도 문자열로 변환된다는 점에 주의해야 한다.
1
2
alert( '1' + 2 ); // "12"
alert( 2 + '1' ); // "21"
단 연산의 순서를 주의해야 한다.
1
alert(2 + 2 + '1' ); // '221'이 아니라 '41'이 출력됩니다.
연산은 왼쪽에서 오른쪽으로 순차적으로 진행되기 때문에 이런 결과가 나왔다. 두 개의 숫자 뒤에 문자열이 오는 경우, 숫자가 먼저 더해지고, 그 후 더해진 숫자와 문자열과의 병합이 일어난다.
+
와 달리, 다른 산술 연산자는 오직 숫자형의 피연산자만 다루고, 피연산자가 숫자형이 아닌 경우에 그 형을 숫자형으로 바꾼다.
1
2
alert( 6 - '2' ); // 4, '2'를 숫자로 바꾼 후 연산이 진행됩니다.
alert( '6' / '2' ); // 3, 두 피연산자가 숫자로 바뀐 후 연산이 진행됩니다.
단항 연산자 +와 숫자형으로의 변환
숫자에 단항 덧셈 연산자를 붙이면 이 연산자는 아무런 동작도 하지 않는다. 그러나 피연산자가 숫자가 아닌 경우엔 숫자형으로의 변환이 일어납니디.
1
2
3
4
5
6
7
8
9
10
// 숫자에는 아무런 영향을 미치지 않는다.
let x = 1;
alert( +x ); // 1
let y = -2;
alert( +y ); // -2
// 숫자형이 아닌 피연산자는 숫자형으로 변화한다.
alert( +true ); // 1
alert( +"" ); // 0
단항 덧셈 연산자는 짧은 문법으로도 Number(...)
와 동일한 일을 할 수 있게 해준다.
개발을 하다 보면 문자열을 숫자로 변환해야 하는 경우가 자주 생긴다. HTML 폼(form) 필드에서 값을 가져왔는데 그 값이 문자형일 때가 그 예시다. 실제로 폼에서 가지고 온 값은 대개 문자열 형태이다.
이항 덧셈 연산자를 사용하면 아래와 같이 값이 문자열로 변해서 연결될 것이다.
1
2
3
4
let apples = "2";
let oranges = "3";
alert( apples + oranges ); // 23, 이항 덧셈 연산자는 문자열을 연결한다.
원하는 대로 값을 더해주려면, 단항 덧셈 연산자를 사용해 피연산자를 숫자형으로 변화시키면 된다.
1
2
3
4
5
6
7
8
let apples = "2";
let oranges = "3";
// 이항 덧셈 연산자가 적용되기 전에, 두 피연산자는 숫자형으로 변화한다.
alert( +apples + +oranges ); // 5
// `Number(...)`를 사용해서 같은 동작을 하는 코드를 작성할 수 있다.
// alert( Number(apples) + Number(oranges) ); // 5
연산자 우선순위
하나의 표현식에 둘 이상의 연산자가 있는 경우, 실행 순서는 연산자의 우선순위(precedence) 에 의해 결정됩니다.
자바스크립트에서 정의한 연산자 우선순위가 마음에 들지 않는다면, 괄호를 사용하면 됩니다. 괄호는 모든 연산자보다 우선순위가 높기 때문에 자바스크립트에서 정의한 연산자 우선순위를 무력화시킵니다. 표현식 (1 + 2) * 2
에서 괄호로 둘러싼 덧셈 연산자가 먼저 수행되는 것 같이 말이죠.
자바스크립트는 다양한 연산자를 제공하는데, 이 모든 연산자엔 우선순위가 매겨져 있습니다. 우선순위 숫자가 클수록 먼저 실행됩니다. 순위가 같으면 왼쪽부터 시작해서 오른쪽으로 연산이 수행됩니다.
아래는 우선순위 테이블(precedence table)의 일부를 발췌한 표입니다. 순서를 기억할 필요는 없지만, 동일한 기호의 단항 연산자는 이항 연산자보다 우선순위가 더 높다는 것에 주목해 주시기 바랍니다.
순위 | 연산자 이름 | 기호 |
---|---|---|
… | … | … |
17 | 단항 덧셈 | + |
17 | 단항 부정 | - |
16 | 지수 | ** |
15 | 곱셈 | * |
15 | 나눗셈 | / |
13 | 덧셈 | + |
13 | 뺄셈 | - |
… | … | … |
3 | 할당 | = |
… | … | … |
할당 연산자
무언가를 할당할 때 쓰이는 =
는 할당(assignment) 연산자라고 불리는데, 우선순위는 3
으로 아주 낮다.
값을 반환하는 할당 연산자
자바스크립트에서 대부분의 연산자들은 값을 반환한다. =
도 마찬가지이다.
x = value
을 호출하면 value
가 x
에 쓰여지고, 이에 더하여 value
가 반환됩니다.
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
위 예제에서 표현식 (a = b + 1)
은 a
에 값을 할당하고, 그 값인 3
을 반환한다. 그리고 반환 값은 이어지는 표현식에 사용된다. 여러 자바스크립트 라이브러리에서 이런 식으로 할당 연산자를 사용하고 있기 때문에 동작 원리를 이해할 수 있어야 한다. 다만 가독성이 떨어지기 때문에 사용하지 않는 것이 좋다.
할당 연산자 체이닝
할당 연산자는 아래와 같이 여러 개를 연결할 수도 있다.
1
2
3
4
5
6
7
let a, b, c;
a = b = c = 2 + 2;
alert( a ); // 4
alert( b ); // 4
alert( c ); // 4
이렇게 할당 연산자를 여러 개 연결한 경우, 평가는 우측부터 진행된다. 먼저 가장 우측의 2 + 2
가 평가되고, 그 결과가 좌측의 c
, b
, a
에 순차적으로 할당된다.
그러나 가독성을 위해 연산자를 체이닝하지 말고 줄을 나눠 코드를 작성하자
1
2
3
c = 2 + 2;
b = c;
a = c;
이렇게 작성하면 읽기도 쉽고, 눈을 빠르게 움직이며 코드를 읽을 수 있다.
복합 할당 연산자
짧은 문법으로 변수에 연산자를 적용하고 그 결과를 같은 변수에 저장해야 할 때 사용한다.
1
2
3
4
5
let n = 2;
n += 5; // n은 7이 됩니다(n = n + 5와 동일).
n *= 2; // n은 14가 됩니다(n = n * 2와 동일).
alert( n ); // 14
복합 할당 연산자의 우선순위는 할당 연산자와 동일하다. 대부분 다른 연산자가 실행된 후에 복합 할당 연산자가 실행된다.
증가·감소 연산자
- 증가(increment) 연산자
++
는 변수를 1 증가시킵니다. 감소(decrement) 연산자
--
는 변수를 1 감소시킵니다.- 증가/감소 연산자는 변수에만 쓸 수 있다. 값에 사용하려고 하면 에러가 발생한다.
전위형과 후위형
- 후위형 증가/감소 연산자(postfix form): 증가/감소 후의 새로운 값을 반환한다.
- 전위형 증가/감소 연산자(prefix form): 증가/감소 전의 기존 값을 반환한다.
모든 연산자는 값을 반환한다.
1) 반환 값을 사용하지 않는 경우라면, 전위형과 후위형엔 차이가 없다.
1 2 3 4
let counter = 0; counter++; ++counter; alert( counter ); // 2, 위 두 라인은 동일한 연산을 수행한다.
2) 값을 증가시키고 난 후, 증가한 값을 바로 사용하려면 전위형 증가 연산자를 사용하면 된다.
1 2
let counter = 0; alert( ++counter ); // 1
3) 값을 증가시키지만, 증가 전의 기존값을 사용하려면 후위형 증가 연산자를 사용하면 된다.
1 2
let counter = 0; alert( counter++ ); // 0
다른 연산자 사이의 증가/감소 연산자
++/--
연산자를 표현식 중간에 사용하는 것도 가능하다. 이때, 증가/감소 연산자의 우선순위는 다른 대부분의 산술 연산자보다 높기 때문에, 평가가 먼저 이뤄진다.
1
2
3
4
5
6
7
let counter = 1;
// 전위
alert( 2 * ++counter ); // 4
// 후위
counter = 1;
alert( 2 * counter++ ); // counter++는 '기존'값을 반환하기 때문에 2가 출력된다.
위 코드는 한 줄에서 여러 가지 일을 동시에 하고 있기 때문에 코드의 가독성이 떨어진다. 코드를 읽을 때 눈을 ‘수직으로’ 빠르게 움직이다 보면 counter++
와 같은 것을 놓치기 쉽다. 변수가 증가했다는 것을 놓칠 수 있는 것이다. 따라서 코드 한 줄엔, 특정 동작 하나에 관련된 내용만 작성하는 게 좋다.
1
2
3
let counter = 1;
alert( 2 * counter );
counter++;
비트 연산자
비트 연산자는 저수준(2진 표현)에서 숫자를 다뤄야 할 때 쓰이므로 흔하게 쓰이진 않는다. 웹 개발 시엔 이런 일이 자주 일어나지 않기 때문에 비트 연산자를 만날 일은 거의 없다. 그렇긴 해도 암호를 다뤄야 할 땐 비트 연산자가 유용하다.
쉼표 연산자(comma operator)
좀처럼 보기 힘들고, 특이한 연산자 중 하나이다. 코드를 짧게 쓰려는 의도로 가끔 사용된다. 쉼표 연산자 ,
는 여러 표현식을 코드 한 줄에서 평가할 수 있게 해준다. 이때 표현식 각각이 모두 평가되지만, 마지막 표현식의 평가 결과만 반환되는 점에 유의해야 한다.
1
2
3
let a = (1 + 2, 3 + 4);
alert( a ); // 7 (3 + 4의 결과)
위 코드에서 첫번째 표현식 1 + 2
은 평가가 되지만 그 결과는 버려진다. 3 + 4
만 평가되어 a
에 할당된다.
쉼표의 우선순위는 매우 낮다.
쉼표 연산자의 연산자 우선순위는 매우 낮다. 할당 연산자 =
보다 더 낮죠. 따라서 위 예시에선 괄호가 중요한 역할을 한다.
괄호가 없으면 a = 1 + 2, 3 + 4
에서 +
가 먼저 수행되어 a = 3, 7
이 됩니다. 할당 연산자 =
는 쉼표 연산자보다 우선순위가 높기 때문에 a = 3
이 먼저 실행되고, 나머지(7
)는 무시되죠. (a = 1 + 2), 3 + 4
를 연산한 것처럼 될 겁니다.
이렇게 마지막 표현식을 제외한 모든 것을 버리는 연산자는 어디서 사용되는 걸까? 여러 동작을 하나의 줄에서 처리하려는 복잡한 구조에서 이를 사용한다.
1
2
3
4
// 한 줄에서 세 개의 연산이 수행됨
for (a = 1, b = 3, c = a * b; a < 10; a++) {
...
}
쉼표 연산자를 사용한 트릭은 여러 자바스크립트 프레임워크에서 볼 수 있다.
쉼표 연산자는 코드 가독성에 도움이 되지 않기 때문에, 진짜 필요한 경우에만 사용해야 한다.
과제
Q1. 아래 코드가 실행된 후, 변수 a
, b
, c
, d
엔 각각 어떤 값들이 저장될까요?
1
2
3
4
let a = 1, b = 1;
let c = ++a; // 2: 증가 후의 값을 리턴
let d = b++; // 1: 증가 전의 값을 리턴
Q2. 아래 코드가 실행되고 난 후, a
와 x
엔 각각 어떤 값이 저장될까요?
1
2
3
4
5
let a = 2;
let x = 1 + (a *= 2);
// x: 5
// a: 4
Q3. 아래 표현식의 결과 예측
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"" + 1 + 0 //10
"" - 1 + 0 //-1
true + false //1
6 / "3" //2
"2" * "3" //6
4 + 5 + "px" //9px
"$" + 4 + 5 //$45
"4" - 2 //2
"4px" - 2 //NaN
7 / 0 //Infinity
" -9 " + 5 // -9 5
" -9 " - 5 //-14
null + 1 //1: 숫자형으로 변환시 null은 0이 된다.
undefined + 1 //NaN: 숫자형으로 변환시 undefined는 NaN이 된다.
" \t \n" - 2
//" \t \n"는 숫자형으로 변환 시 길이가 0인 문자열로 취급되어 숫자 0이 된다.
Q4. 예시가 제대로 동작하도록 코드를 수정해 보세요. 결과는 3
이 되어야 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let a = prompt("덧셈할 첫 번째 숫자를 입력해주세요.", 1);
let b = prompt("덧셈할 두 번째 숫자를 입력해주세요.", 2);
alert(a + b); // 12
//해답
alert(+a + +b);
// Prompt의 리턴값은 문자열이다.
// 이항덧셈연산자는 문자열을 더하였을 때 문자열을 열결해서 반환한다.
// 이항덧셈연산자가 더하기 전에 각 문자열을 숫자로 바꾸어야 한다.
// 단항덧셈연산자는 숫자에 붙었을 때는 아무 일도 안하지만 문자에 붙었을 때는 숫자를 리턴한다.
// 즉 Number(a)와 a++는 같다.
// 1)
// alert(+a + +b);
// 2)
// let a = +prompt("덧셈할 첫 번째 숫자를 입력해주세요.", 1);
// let b = +prompt("덧셈할 두 번째 숫자를 입력해주세요.", 1);