서론
연산자는 하나 이상의 표현식을 대상으로 산술, 할당, 비교, 논리, 타입, 지수 연산 등을 수행해 하나의 값을 만든다. 이때 연산의 대상을 피연산자라고 한다.
피연산자는 값으로 평가될 수 있는 표현식이어야 한다. 그리고 피연산자와 연산자의 조합으로 이뤄진 연산자 표현식도 값으로 평가될 수 있는 표현식이다.
연산자는 "피연산자를 연산하여 새로운 값을 만든다"는 동사의 역할을 하고, 피연산자는 "값"이라는 명사의 역할을 한다고 볼 수 있다.
// 산술 연산자
5 * 4 // -> 20
// 문자열 연결 연산자
'My name is ' + 'Lee' // -> 'My name is Lee'
// 할당 연산자
color = 'red' // -> 'red'
// 비교 연산자
3 > 5 // -> false
// 논리 연산자
true && false // -> false
// 타입 연산자
typeof 'Hi' // -> string
즉, 연산자는 프로그래밍 언어의 핵심적인 요소로서, 다양한 연산을 수행하여 하나의 값을 만들어내는 기능을 한다. 연산자를 사용할 때는 피연산자가 값으로 평가될 수 있는 표현식인지 확인해야 하며, 피연산자와 연산자의 조합으로 이뤄진 연산자 표현식 역시 값으로 평가될 수 있어야 한다.
산술 연산자
산술 연산자는 피연산자를 대상으로 수학적 계산을 수행해 새로운 숫자 값을 만든다. 만약 산술 연산이 불가능한 경우는 NaN을 반환한다.
이항 산술 연산자
5 + 2; // 7
5 - 2; // 3
5 * 2; // 10
5 / 2; // 2.5
5 % 2; // 1
이항 산술 연산자는 2개의 피연산자를 산술 연산하여 숫자 값을 만든다. 모든 이항 산술 연산자는 피연산자의 값을 변경하는 부수 효과는 없다. 다시 말해, 어떤 산술 연산을 해도 피연산자의 값이 바뀌지는 않고 언제나 새로운 값을 만들어 낸다.
- 덧셈 연산자(+)
- 뺄셈 연산자(-)
- 곱셈 연산자(*)
- 나눗셈 연산자(/)
- 나머지 연산자(%)
이항 산술 연산자에는 위와 같은 종류가 있다.
단항 산술 연산자
let x = 1;
x++; // x = x + 1과 동일하다. x의 값이 변경됨
console.log(x); // 2
x--; // x = x - 1과 동일하다. x의 값이 변경됨
console.log(x); // 1
단항 산술 연산자는 1개의 피연산자를 산술 연산하여 숫자 값을 만든다.
주의할 점은 증가/감소(++/--) 연산자는 피연산자의 값을 변경하는 부수 효과가 있다는 것이다. 다시 말해, 증가/감소 연산을 하면 피연산자의 값을 변경하는 암묵적 할당이 이뤄진다.
let x = 5, result;
// 선할당 후증가(postfix increment operator)
result = x++;
console.log(result, x); // 5 6
// 선증가 후할당(prefix increment operator)
result = ++x;
console.log(result, x); // 7 7
// 선할당 후감소(postfix decrement operator)
result = x--;
console.log(result, x); // 7 6
// 선감소 후할당(prefix decrement operator)
result = --x;
console.log(result, x); // 5 5
증가/감소 연산자는 위치에 따라 의미가 달라진다.
- 피연산자 앞에 위치한 전위 증가/감소 연산자(prefix increment/decrement operator)는 먼저 피연산자의 값을 증가/감소시킨 후, 다른 연산을 수행한다.
- 피연산자 뒤에 위치한 후위 증가/감소 연산자(postfix increment/decrement operator)는 먼저 다른 연산을 수행한 후, 피연산자의 값을 증가/감소시킨다.
// 문자열을 숫자로 타입 변환
+`1`; // 1
// 불리언 값을 숫자로 타입 변환
+true; // 1
+false; // 0
// 문자열을 숫자로 변환할 수 없으면 NaN 반환
+`hello`; // NaN
// 부호를 반전
-(-10); // 10
-`10`; // -10
-true; // -1
-`hello`; // NaN
한편, +와 - 단항 연산자는 숫자 타입이 아닌 피연산자에 사용하면 피연산자를 숫자 타입으로 변환하여 반환한다. 이때 피연산자를 직접 변경하지는 않고 숫자 타입으로 변환한 값을 생성해 반환하므로 부수 효과는 없다.
문자열 연결 연산자
+연산자는 피연산자 중 하나 이상이 문자열인 경우 문자열 연결 연산자로 동작한다. 그 외에는 산술 연산자로 동작한다.
// 문자열 연결 연산자
`1` + 2; // '12'
1 + `2`; // '12'
// 산술 연산자
1 + 2; // 3
// true는 1로 타입 변환된다.
1 + true; // 2
// false는 0으로 타입 변환된다.
1 + false; // 1
// null은 0으로 타입 변환된다.
1 + null; // 1
// undefined는 숫자로 변환되지 않는다.
+undefined; // NaN
1 + undefined; // NaN
위 예제에서 보듯이 1 + true를 연산하면 자바스크립트 엔진은 불리언 타입의 값인 true를 숫자 타입인 1로 타입을 강제로 변환한 후 연산을 수행한다. 이처럼 개발자의 의도와는 상관없이 자바스크립트 엔진에 의해 암묵적으로 타입이 자동 변환되기도 하는데, 이를 암묵적 타입 변환 또는 타입 강제 변환이라 한다.
할당 연산자
할당 연산자는 우항에 있는 피연산자의 평가 결과를 좌항에 있는 변수에 할당한다. 할당 연산자는 좌항의 변수에 값을 할당하므로 변수의 값이 변경되는 부수 효과가 있다.
var x;
x = 10;
console.log(x); // 10
x += 5; // x = x + 5;
console.log(x); // 15
x -= 5; // x = x - 5;
console.log(x); // 10
x *= 5; // x = x * 5;
console.log(x); // 50
x /= 5; // x = x / 5;
console.log(x); // 10
x %= 5; // x = x % 5;
console.log(x); // 0
var str = "My name is ";
str += "Lee"; // str = str + "Lee";
console.log(str); // "My name is Lee"
위 예제와 같이 할당 연산자는 아래와 같은 종류가 있다.
할당 연산자 | 예 | 동일 표현 | 부수 효과 |
= | x = 5 | x = 5 | O |
+= | x += 5 | x = x + 5 | O |
-= | x -= 5 | x = x - 5 | O |
*= | x *= 5 | x = x * 5 | O |
/= | x /= 5 | x = x / 5 | O |
%= | x %= 5 | x = x % 5 | O |
할당문은 값으로 평가되는 표현식인 문으로서 할당된 값으로 평가된다. 따라서 아래와 같이 여러 변수에 동일한 값을 연쇄적으로 할당할 수 있다.
var a, b, c;
// 연쇄 할당. 오른쪽에서 왼쪽으로 진행.
// ① c = 0 : 0으로 평가된다.
// ② b = 0 : 0으로 평가된다.
// ③ a = 0 : 0으로 평가된다.
a = b = c = 0;
console.log(a, b, c); // 0 0 0
할당 연산자는 좌항의 변수에 값을 할당하므로 일반적으로 반복문 내에서 반복시마다 연산 결과를 누적시키기 위해 사용되곤 한다. 예를 들어, += 연산자는 변수에 값을 더한 결과를 다시 변수에 할당하여 누적 합을 구할 때 유용하게 사용된다.
var sum = 0;
for (var i = 1; i <= 10; i++) {
sum += i;
}
console.log(sum); // 55
이처럼 할당 연산자는 변수의 값을 변경하는 부수 효과를 가지므로 예기치 않은 결과를 초래할 수 있어 주의가 필요하다. 특히 연쇄 할당의 경우, 오른쪽에서부터 왼쪽 방향으로 할당이 이뤄지므로 값의 변경에 유의해야 한다.
비교 연산자
비교 연산자는 좌항과 우항의 피연산자를 비교한 다음 그 결과를 불리언 값으로 반환한다. 비교 연산자는 if 문이나 for 문과 같은 제어문의 조건식에서 주로 사용된다.
동등/일치 비교 연산자
비교 연산자 | 의미 | 사례 | 설명 | 부수 효과 |
== | 동등 비교 | x == y | x와 y의 값이 같음 | X |
=== | 일치 비교 | x === y | x와 y의 값과 타입이 같음 | X |
!= | 부동등 비교 | x != y | x와 y의 값이 다름 | X |
!== | 불일치 비교 | x !== y | x와 y의 값과 타입이 다름 | X |
동등 비교(==) 연산자와 일치 비교(===) 연산자는 좌항과 우항의 피연산자가 같은 값으로 평가되는지 비교해 불리언 값을 반환한다. 하지만 비교하는 엄격성의 정도가 다르다.
동등 비교(==) 연산자는 좌항과 우항의 피연산자를 비교할 때 먼저 암묵적 타입 변환을 통해 타입을 일치시킨 후 같은 값인지 비교한다.
일치 비교(===) 연산자는 좌항과 우항의 피연산자가 타입도 같고 값도 같은 경우에 한하여 true를 반환한다.
// 동등 비교. 결과를 예측하기 어려움
`0` == ``; // false
0 == ``; // true
0 == `0`; // true
false == `false`; // false
false == null; // false
false == undefined; // false
동등 비교(==) 연산자는 편리해 보이지만 결과를 예측하기 어렵다.
// 일치 비교
5 === 5; // true
5 === `5`; // false
따라서 동등 비교 연산자 대신 일치 비교 연산자를 사용하는 것이 좋다.
단, 일치 비교 연산자도 주의해야 할 사항이 있다.
// NaN은 자신과 일치하지 않는 유일한 값
NaN === NaN; // false
Number.isNaN(NaN); // true
Number.isNaN(10); // false
Number.isNaN(1 + undefined); // true
NaN은 자신과 일치하지 않는 유일한 값이다. 따라서 숫자가 NaN인지 조사하려면 빌트인 함수 Number.isNaN을 사용해야 한다.
// 양의 0과 음의 0의 비교
0 === -0; // true
0 == -0; // true
// Object.is는 예측 가능한 정확한 비교 결과를 반환한다.
Object.is(-0, +0); // false
Object.is(NaN, NaN); // true
+0과 -0을 비교하면 일치 비교/동등 비교 모두 결과가 true다. 이는 예측하기 어려운 결과인데 ES6에서 도입된 Object.is 메서드를 사용하면 정확한 비교 결과를 반환한다.
한편, 부동등 비교(!=) 연산자와 불일치 비교(!==) 연산자는 각각 동등 비교(==) 연산자와 일치 비교(===) 연산자의 반대 개념이다.
대소 관계 비교 연산자
// 대소 관계 비교
5 > 0; // true
5 > 5; // false
5 >= 5; // true
5 <= 5; // true
대소 관계 비교 연산자는 피연산자의 크기를 비교하여 불리언 값을 반환한다.
대소 관계 비교 연산자 | 예제 | 설명 | 부수 효과 |
> | x > y | x가 y보다 크다 | X |
< | x < y | x가 y보다 작다 | X |
>= | x >= y | x가 y보다 크거나 같다 | X |
<= | x <= y | x가 y보다 작거나 같다 | X |
이상의 내용을 종합해 볼 때 비교 연산자는 주로 조건문에 사용되며, 특히 동등 비교 연산자(==)보다 일치 비교 연산자(===)를 사용하는 것이 좋고, 양의 0과 음의 0을 정확히 비교하려면 ES6에서 도입된 Object.is 메서드를 사용하는 것이 좋다.
삼항 조건 연산자
삼항 조건 연산자(ternary operator)는 조건식의 평가 결과에 따라 반환할 값을 결정한다. 자바스크립트의 유일한 삼항 연산자이며, 부수 효과는 없다.
삼항 조건 연산자의 기본 형태는 아래와 같다.
조건식 ? 조건식이 true일 때 반환할 값 : 조건식이 false일 때 반환할 값
물음표(?) 앞의 첫 번째 피연산자는 boolean 타입의 값으로 평가될 표현식(조건식)이다. 만약 조건식의 평가 결과가 boolean 값이 아니면 boolean 값으로 암묵적 타입 변환된다.
var x = 2;
// 2 % 2는 0이고 0은 false로 암묵적 타입 변환된다.
var result = x % 2 ? '홀수' : '짝수';
console.log(result); // 짝수
위 예제처럼 조건식 x % 2의 평가 결과는 0이다. 이때 0은 false로 암묵적 타입 변환되므로 result 변수에는 '짝수'가 할당된다.
var x = 2, result;
if (x % 2) result = '홀수';
else result = '짝수';
console.log(result); // 짝수
삼항 조건 연산자는 위처럼 if...else 문으로도 유사하게 처리할 수 있다.
var x = 10;
// if...else 문은 표현식이 아닌 문이다. 따라서 값처럼 사용할 수 없다.
var result = if (x % 2) {result = '홀수';} else {result = '짝수';};
// SyntaxError: Unexpected token if
하지만 삼항 조건 연산자 표현식과 if...else 문은 중요한 차이가 있다. 삼항 조건 연산자 표현식은 값처럼 사용할 수 있지만, if...else 문은 값처럼 사용할 수 없다. if...else 문은 표현식이 아니라 문(statement)이기 때문이다.
var x = 10;
// 삼항 조건 연산자 표현식은 표현식인 문이다. 따라서 값처럼 사용할 수 있다.
var result = x % 2 ? '홀수' : '짝수';
console.log(result); // 짝수
반면 삼항 조건 연산자 표현식은 값으로 평가할 수 있는 표현식인 문이다. 따라서 아래처럼 변수에 할당할 수도 있고, 다른 표현식의 일부가 될 수도 있어 매우 유용하다.
조건에 따라 어떤 값을 결정해야 한다면 삼항 조건 연산자 표현식을 사용하는 편이 좋고, 조건에 따라 수행해야 할 문이 여러 개라면 if...else 문의 가독성이 더 좋다.
논리 연산자
논리 연산자 | 의미 | 부수 효과 |
|| | 논리합(OR) | X |
&& | 논리곱(AND) | X |
! | 부정(NOT) | X |
논리 연산자(logical operator)에는 위와 같은 종류가 있다.
// 논리합(||) 연산자
true || true; // true
true || false; // true
false || true; // true
false || false; // false
// 논리곱(&&) 연산자
true && true; // true
true && false; // false
false && true; // false
false && false; // false
// 논리 부정(!) 연산자
!true; // false
!false; // true
논리 연산자는 우항과 좌항의 피연산자(부정 논리 연산자의 경우 우항의 피연산자)를 논리 연산한다.
// 암묵적 타입 변환
!0; // true
!'Hello'; // false
논리 부정(!) 연산자는 언제나 불리언 값을 반환한다. 단, 피연산자가 반드시 불리언 값일 필요는 없다. 만약 피연산자가 불리언 값이 아니면 불리언 타입으로 암묵적 타입 변환된다.
// 단축 평가
'Cat' && 'Dog'; // 'Dog'
한편, 논리합(||) 또는 논리곱(&&) 연산자 표현식의 평가 결과는 불리언 값이 아닐 수도 있다. 논리합(||) 또는 논리곱(&&) 연산자 표현식은 언제나 2개의 피연산자 중 어느 한쪽으로 평가된다. 이를 단축 평가라 한다.
// 드 모르간의 법칙
!(x || y) === (!x && !y)
!(x && y) === (!x || !y)
논리 연산자로 구성된 복잡한 표현식은 가독성이 좋지 않아 한눈에 이해하기 어려울 때가 있다. 이러한 경우 드 모르간의 법칙을 활용하면 복잡한 표현식을 좀 더 가독성 좋게 변환할 수 있다.
!(x || y) === (!x && !y)
위의 예시를 보자. 드 모르간 법칙에 의하면 'x가 아니면서 y도 아닌 것'은 '(x 또는 y)의 논리 부정'과 같다.
!(x && y) === (!x || !y)
다음 위의 예시도 살펴보자. 이는 'x가 아니거나 y가 아닌 것'은 '(x와 y)의 논리 부정'과 같음을 나타낸다.
이처럼 논리 연산자는 주로 불리언 값을 만드는 데 사용되지만, 피연산자의 값에 따라 불리언이 아닌 값을 반환하는 경우도 있다. 특히 단축 평가의 경우 실제 어플리케이션에서 매우 유용하게 사용되므로 확실히 이해해두어야 한다.
쉼표 연산자
var x, y, z;
x = 1, y = 2, z = 3; // 3
쉼표(,) 연산자는 왼쪽 피연산자부터 차례대로 피연산자를 평가하고 마지막 피연산자의 평가가 끝나면 마지막 피연산자의 평가 결과를 반환한다.
위 예제에서 쉼표 연산자는 첫 번째 피연산자 x = 1을 평가하고, 두 번째 피연산자 y = 2를 평가한 후, 마지막 피연산자 z = 3을 평가하고 마지막 평가 결과인 3을 반환한다.
function myFunc() {
var x = 0;
return (x += 1, x); // return ++x;와 같은 동작을 한다.
}
console.log(myFunc()); // 1
이를 응용하여 위와 같은 함수를 만들 수 있다.
그룹 연산자
10 * 2 + 3; // 23
10 * (2 + 3); // 50
그룹 연산자 『()』는 자신의 피연산자인 표현식을 가장 먼저 평가한다. 따라서 그룹 연산자를 사용하면 연산자 우선순위를 조절할 수 있다. 그룹 연산자는 연산자 우선순위가 가장 높다.
위 예제에서 첫 번째 문은 * 연산자가 + 연산자보다 우선순위가 높기 때문에 10 * 2가 먼저 실행되고 그 결과에 3을 더해 23이 된다. 두 번째 문은 그룹 연산자로 + 연산을 먼저 실행하도록 했기 때문에 2 + 3이 먼저 실행되고 그 결과에 10을 곱해 50이 된다.
typeof 연산자
typeof ''; // "string"
typeof 1; // "number"
typeof NaN; // "number"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof Symbol(); // "symbol"
typeof null; // "object"
typeof function() {}; // "function"
typeof 연산자는 피연산자의 데이터 타입을 문자열로 반환한다. typeof 연산자는 7가지 문자열 "string", "number", "boolean", "undefined", "symbol", "object", "function" 중 하나를 반환한다. "null"을 반환하는 경우는 없다.
var foo = null;
typeof foo === null; // false
foo === null; // true
typeof 연산자로 null 값을 연산해보면 "null"이 아닌 "object"를 반환한다. 이는 자바스크립트의 초기 설계 결함에 의한 것으로, 하위 호환성을 유지하기 위해 수정되지 않고 있다.
다만, 이는 조금 모호한 측면이 있다. 지금 시점에서는 설계 결함이라고 할 수 있지만, 자바스크립트 개발 당시의 시점에는 불가피한 측면(?)이 조금 있었다. 자바스크립트의 설계자인 브랜든 아이크는 null을 객체(Object)의 일종으로 취급하려 했기 때문에 typeof null이 "object"를 반환하도록 의도적으로 설계했다. 정작 이후 들어서 typeof가 추가되고, 문제점이 부각되고, 대표적인 버그이자 뭍혀져가는 구멍으로 남아버렸지만 말이다.
아무튼, 값이 null 타입인지 확인할 때는 typeof 연산자 대신 일치 연산자(===)를 사용해야 한다.
// undeclared 식별자를 선언한 적이 없다.
typeof undeclared; // undefined
또한 typeof 연산자는 선언하지 않은 식별자와 호출하면 ReferenceError가 발생하지 않고 undefined를 반환한다.
지수 연산자
2 ** 2; // 4
2 ** 2.5; // 5.65685424949238
2 ** 0; // 1
2 ** -2; // 0.25
ES7에서 도입된 지수 연산자는 좌항의 피연산자를 밑(base)으로, 우항의 피연산자를 지수(exponent)로 거듭 제곱하여 숫자 값을 반환한다. 지수 연산자의 결과는 숫자 값으로 반환된다.
(-5) ** 2; // 25
음수를 거듭제곱의 밑으로 사용해 계산하려면 괄호()로 묶어야 한다.
2 * 5 ** 2; // 50
지수 연산자는 이항 연산자 중에서 우선순위가 가장 높다. 위 예제에서 지수 연산자 **의 우선순위가 산술 연산자 *보다 높기 때문에 5 ** 2가 먼저 실행된 후, 그 결과인 25에 2를 곱해 50이 된다.
Math.pow(2, 2); // 4
Math.pow(2, 2.5); // 5.65685424949238
Math.pow(2, 0); // 1
Math.pow(2, -2); // 0.25
지수 연산자가 도입되기 이전에는 Math.pow 메서드를 사용했었다. 하지만 지수 연산자가 가독성 측면에서 더 좋다.
이상으로 쉼표 연산자, 그룹 연산자, typeof 연산자, 지수 연산자에 대해 살펴보았다. 쉼표 연산자는 왼쪽 피연산자부터 차례대로 평가하고 마지막 평가 결과를 반환하고, 그룹 연산자는 연산자 우선순위를 조절할 수 있다. typeof 연산자는 7가지 문자열 중 하나를 반환하며 null 확인시에는 주의가 필요하다.
마지막으로 ES7에서 도입된 지수 연산자는 거듭제곱을 할 때 가독성 좋게 사용할 수 있다.
그 외의 연산자
연산자 | 개요 | 참고 |
?. | 옵셔널 체이닝 연산자 | 9.4.2절 |
?? | null 병합 연산자 | 9.4.3절 |
delete | 프로퍼티 삭제 | 10.8절 |
new | 생성자 함수를 호출할 때 사용하여 인스턴스를 생성 | 17.2.6절 |
instanceof | 좌변의 객체가 우변의 생성자 함수와 연결된 인스턴스인지 판별 | 19.10절 |
in | 프로퍼티 존재 확인 | 19.13.1절 |
자바스크립트에는 앞에서 살펴본 연산자 외에도 다양한 연산자가 존재한다. 그중 일부를 간단히 소개하면 아래와 같다.
- 옵셔널 체이닝 연산자 『?.』는 좌항의 피연산자가 null 또는 undefined인 경우 undefined를 반환하고, 그렇지 않으면 우항의 프로퍼티 참조를 이어간다.
- null 병합 연산자 『??』 는 좌항의 피연산자가 null 또는 undefined인 경우 우항의 피연산자를 반환하고, 그렇지 않으면 좌항의 피연산자를 반환한다.
- delete 연산자는 객체의 프로퍼티를 삭제한다.
- new 연산자는 생성자 함수를 호출할 때 사용하여 인스턴스를 생성한다.
- instanceof 연산자는 좌변의 객체가 우변의 생성자 함수와 연결된 인스턴스인지 판별하여 true 또는 false를 반환한다.
- in 연산자는 객체에 특정 프로퍼티가 존재하는지 확인하여 true 또는 false를 반환한다.
이 연산자들은 각각 고유한 기능과 사용 목적을 가지고 있으며, 자바스크립트 코드를 작성할 때 필요에 따라 적절히 활용할 수 있어야 한다.
연산자의 부수 효과
대부분의 연산자는 다른 코드에 영향을 주지 않는다. 예를 들어 1 * 2와 같은 산술 연산은 코드에 어떠한 영향도 주지 않고 단지 새로운 값 2를 생성할 뿐이다.
하지만 일부 연산자는 다른 코드에 영향을 주는 부수 효과(side effect)가 있다. 부수 효과가 있는 연산자로는 할당 연산자(=), 증가/감소 연산자(++/--), delete 연산자가 있다.
var x;
// 할당 연산자는 변수 값이 변하는 부수 효과가 있다.
// 이는 x 변수를 사용하는 다른 코드에 영향을 준다.
x = 1;
console.log(x); // 1
// 증감 연산자는 피연산자의 값을 변경하는 부수 효과가 있다.
// 사실상 피연산자 변수 x에 값이 재할당되는 것이다.
// 이는 x 변수를 사용하는 다른 코드에 영향을 준다.
x++;
console.log(x); // 2
var o = { a: 1 };
// delete 연산자는 객체의 프로퍼티를 삭제하는 부수 효과가 있다.
// 이는 o 객체를 사용하는 다른 코드에 영향을 준다.
delete o.a;
console.log(o); // {}
위 예제를 보면, 할당 연산자 =는 변수 x의 값을 변경하는 부수 효과가 있다. 이는 x 변수를 사용하는 다른 코드에 영향을 줄 수 있다.
증감 연산자 ++는 피연산자인 변수 x의 값을 재할당하여 변경하는 부수 효과가 있다. 이 역시 x 변수를 사용하는 다른 코드에 영향을 줄 수 있다.
delete 연산자는 객체 o의 프로퍼티 a를 삭제하는 부수 효과가 있다. 이는 o 객체를 사용하는 다른 코드에 영향을 줄 수 있다.
이처럼 일부 연산자는 피연산자의 값을 직접 변경하는 부수 효과가 있으므로, 코드를 작성할 때는 연산자의 부수 효과를 염두에 두고 신중하게 사용해야 한다. 부수 효과로 인해 예기치 않은 결과가 발생할 수 있기 때문이다.
연산자 우선순위
우선순위 | 연산자 |
1 | () |
2 | new(매개변수 존재), . , [](프로퍼티 접근), ()(함수 호출), ?.(옵셔널 체이닝 연산자) |
3 | new(매개변수 미존재) |
4 | x++, x-- |
5 | !x, +x, -x, ++x, --x, typeof, delete |
6 | **(이항 연산자 중에서 우선순위가 가장 높다) |
7 | *, /, % |
8 | +, - |
9 | <, <=, >, >=, in, instanceof |
10 | ==, !=, ===, !== |
11 | ??(null 병합 연산자) |
12 | && |
13 | || |
14 | ? ... : ...(삼항 연산자) |
15 | 할당 연산자(=, +=, -=, ...) |
16 | , |
자바스크립트에서는 여러 개의 연산자가 사용된 문이 실행될 때 연산자의 우선순위에 따라 실행 순서가 결정된다.
위 표에서 볼 수 있듯이, 그룹 연산자 ()가 가장 높은 우선순위를 가지며, 쉼표 연산자 ,가 가장 낮은 우선순위를 가진다. 하지만 연산자의 우선순위를 모두 외우는 것은 쉽지 않다. 따라서 연산자 우선순위가 가장 높은 그룹 연산자 ()를 사용하여 우선순위를 명시적으로 조절하는 것이 좋다. 예를 들어 3 ** 1 + 2를 27이 되도록 하려면 (3 ** 1) + 2 또는 3 ** (1 + 2)와 같이 그룹 연산자로 감싸주면 된다.
또한, 수학과 마찬가지로 곱셈(*), 나눗셈(/), 나머지(%) 연산을 먼저 평가한 후, 덧셈(+), 뺄셈(-) 연산을 평가하고, 마지막으로 좌항과 우항의 부등호 비교 연산을 평가한다는 점을 기억하면 우선순위를 어느 정도 예측할 수 있다.
연산자 결합 순서
결합 순서 | 연산자 |
좌항 → 우항 | +, -, /, %, <, <=, >, >=, &&, ||, ., [], (), ??, ?., in, instanceof |
우항 → 좌항 | ++, --, 할당 연산자(=, +=, -=, ...), !x, +x, -x, ++x, --x, typeof, delete, ? ... : ..., ** |
연산자 결합 순서란 좌항과 우항 중 어느 쪽부터 평가를 수행할 것인지를 나타내는 순서를 말한다.
예를 들어 1 + 2 < 15 - 1 && 55 % 5 === 0의 경우, 좌항에서 우항으로 연산이 진행된다. 즉, 1 + 2를 먼저 평가한 후, 15 - 1을 평가하고, 그 다음 < 연산을 수행한다. 그리고 55 % 5를 평가한 후 === 연산을 수행하고, 마지막으로 && 연산을 수행하여 최종 결과를 얻는다. 반면 ++x나 x++와 같은 증감 연산자, 할당 연산자, delete, typeof 등은 우항에서 좌항으로 평가가 이뤄진다.
'🧱 프론트엔드 주제 > JavaScript' 카테고리의 다른 글
[모던 자바스크립트 Deep Dive] 9장 타입 변화와 단축 평가 (1) | 2024.06.20 |
---|---|
[모던 자바스크립트 Deep Dive] 8장 제어문 (1) | 2024.06.16 |
[모던 자바스크립트 Deep Dive] 6장 데이터 타입 (1) | 2024.06.10 |
[모던 자바스크립트 Deep Dive] 5장 표현식과 문 (0) | 2024.06.07 |
[모던 자바스크립트 Deep Dive] 4장 변수 (1) | 2024.06.03 |